The goal of this script is to generate a Seurat object for sample 2022_02.

  • removal of cells based on quality control metrics
  • normalization with LogNormalize, then doublets detection using scran hybrid and scDblFinder method, and doublet cells removal
  • normalization with LogNormalize, for only the remaining cells
  • cell cycle and cell type annotation
  • dimensionality reduction using PCA
  • projection using tSNE and UMAP
library(dplyr)
library(patchwork)
library(ggplot2)

.libPaths()
## [1] "/usr/local/lib/R/library"

Preparation

In this section, we set the global settings of the analysis. We will store data there :

out_dir = "."

We load the parameters :

sample_name = params$sample_name # "2021_31"
# sample_name = "2021_31"

Input count matrix is there :

count_matrix_dir = paste0(out_dir, "/input/", sample_name)

We load the markers and specific colors for each cell type :

cell_markers = readRDS(paste0(out_dir, "/../1_metadata/hs_hd_cell_markers.rds"))
cell_markers = lapply(cell_markers, FUN = toupper)
lengths(cell_markers)
##      CD4 T cells      CD8 T cells Langerhans cells      macrophages 
##               13               13                9               10 
##          B cells          cuticle           cortex          medulla 
##               16               15               16               10 
##              IRS    proliferative              IBL              ORS 
##               16               20               15               16 
##              IFE             HFSC      melanocytes        sebocytes 
##               17               17               10                8

Here are custom colors for each cell type :

color_markers = readRDS(paste0(out_dir, "/../1_metadata/hs_hd_color_markers.rds"))

data.frame(cell_type = names(color_markers),
           color = unlist(color_markers)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers), breaks = names(color_markers)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank())

We load markers to display on the dotplot :

dotplot_markers = readRDS(paste0(out_dir, "/../1_metadata/hs_hd_dotplot_markers.rds"))
dotplot_markers = lapply(dotplot_markers, FUN = toupper)
dotplot_markers
## $`CD4 T cells`
## [1] "CD3E" "CD4" 
## 
## $`CD8 T cells`
## [1] "CD3E" "CD8A"
## 
## $`Langerhans cells`
## [1] "CD207" "CPVL" 
## 
## $macrophages
## [1] "TREM2" "MSR1" 
## 
## $`B cells`
## [1] "CD79A" "CD79B"
## 
## $cuticle
## [1] "KRT32" "KRT35"
## 
## $cortex
## [1] "KRT31" "PRR9" 
## 
## $medulla
## [1] "BAMBI"   "ADLH1A3"
## 
## $IRS
## [1] "KRT71" "KRT73"
## 
## $proliferative
## [1] "TOP2A" "MCM5" 
## 
## $IBL
## [1] "KRT16" "KRT6C"
## 
## $ORS
## [1] "KRT15" "GPX2" 
## 
## $IFE
## [1] "SPINK5" "LY6D"  
## 
## $HFSC
## [1] "DIO2"   "TCEAL2"
## 
## $melanocytes
## [1] "DCT"   "MLANA"
## 
## $sebocytes
## [1] "CLMP"  "PPARG"

We load metadata for this sample :

sample_info = readRDS(paste0(out_dir, "/../1_metadata/hs_hd_sample_info.rds"))
sample_info %>%
  dplyr::filter(project_name == sample_name)
##   project_name sample_type sample_identifier gender   color
## 1      2022_02          HD              HD_2      F #3A5FCD

These is a parameter for different functions :

cl = aquarius::create_parallel_instance(nthreads = 3L)
cut_log_nCount_RNA = 6
cut_nFeature_RNA = 500
cut_percent.mt = 20
cut_percent.rb = 50

Load count matrix

In this section, we load the raw count matrix. Then, we applied an empty droplets filtering.

sobj = aquarius::load_sc_data(data_path = count_matrix_dir,
                              sample_name = sample_name,
                              my_seed = 1337L)
## [1]   27955 6794880
## [1] 49660216
## [1] 27955  3612
## [1] 44875419
## [1] 0.9036493
sobj
## An object of class Seurat 
## 27955 features across 3612 samples within 1 assay 
## Active assay: RNA (27955 features, 0 variable features)

(Time to run : 123.93 s)

In genes metadata, we add the Ensembl ID. The sobj@assays$RNA@meta.features dataframe contains three information :

  • rownames : gene names stored as the dimnames of the count matrix. Duplicated gene names will have a .1 at the end of their name
  • Ensembl_ID : EnsemblID, as stored in the features.tsv.gz file
  • gene_name : gene_name, as stored in the features.tsv.gz file. Duplicated gene names will have the same name.
features_df = read.csv(paste0(count_matrix_dir, "/features.tsv.gz"), sep = "\t", header = 0)
features_df = features_df[, c(1:2)]
colnames(features_df) = c("Ensembl_ID", "gene_name")
rownames(features_df) = rownames(sobj) # mandatory for Seurat::FindVariableFeatures

sobj@assays$RNA@meta.features = features_df
rm(features_df)

head(sobj@assays$RNA@meta.features)
##                  Ensembl_ID   gene_name
## MIR1302-2HG ENSG00000243485 MIR1302-2HG
## FAM138A     ENSG00000237613     FAM138A
## OR4F5       ENSG00000186092       OR4F5
## AL627309.1  ENSG00000238009  AL627309.1
## AL627309.3  ENSG00000239945  AL627309.3
## AL627309.4  ENSG00000241599  AL627309.4

We add the same columns as in metadata :

row_oi = (sample_info$project_name == sample_name)

sobj$project_name = sample_name
sobj$sample_identifier = sample_info[row_oi, "sample_identifier"]
sobj$sample_type = sample_info[row_oi, "sample_type"]

colnames(sobj@meta.data)
## [1] "orig.ident"        "nCount_RNA"        "nFeature_RNA"     
## [4] "log_nCount_RNA"    "project_name"      "sample_identifier"
## [7] "sample_type"

Before filtering

Normalization

sobj = Seurat::NormalizeData(sobj,
                             normalization.method = "LogNormalize",
                             assay = "RNA")

sobj = Seurat::FindVariableFeatures(sobj,
                                    assay = "RNA",
                                    nfeatures = 3000)
sobj
## An object of class Seurat 
## 27955 features across 3612 samples within 1 assay 
## Active assay: RNA (27955 features, 3000 variable features)

Projection

We generate a tSNE to visualize cells before filtering.

sobj = aquarius::dimensions_reduction(sobj = sobj,
                                      assay = "RNA",
                                      reduction = "pca",
                                      max_dims = 100,
                                      verbose = FALSE)
Seurat::ElbowPlot(sobj, ndims = 100, reduction = "RNA_pca")

We generate a tSNE with 20 principal components :

ndims = 20
sobj = Seurat::RunTSNE(sobj,
                       reduction = "RNA_pca",
                       dims = 1:ndims,
                       seed.use = 1337L,
                       reduction.name = paste0("RNA_pca_", ndims, "_tsne"))

sobj
## An object of class Seurat 
## 27955 features across 3612 samples within 1 assay 
## Active assay: RNA (27955 features, 3000 variable features)
##  2 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne

Cell type

We annotate cells for cell type using Seurat::AddModuleScore function.

sobj = aquarius::cell_annot_custom(sobj,
                                   newname = "cell_type",
                                   markers = cell_markers,
                                   use_negative = TRUE,
                                   add_score = TRUE,
                                   verbose = TRUE)

colnames(sobj@meta.data) = stringr::str_replace_all(string = colnames(sobj@meta.data),
                                                    pattern = " ",
                                                    replacement = "_")

sobj$cell_type = factor(sobj$cell_type, levels = names(cell_markers))

table(sobj$cell_type)
## 
##      CD4 T cells      CD8 T cells Langerhans cells      macrophages 
##              141               75               55               50 
##          B cells          cuticle           cortex          medulla 
##               40              475               78              233 
##              IRS    proliferative              IBL              ORS 
##              149              699              222              383 
##              IFE             HFSC      melanocytes        sebocytes 
##               81              291              609               31

(Time to run : 26.44 s)

To justify cell type annotation, we can make a dotplot :

markers = c("PTPRC", "MSX2", "KRT16",
            unique(unlist(dotplot_markers[levels(sobj$cell_type)])))
markers = markers[markers %in% rownames(sobj)]

aquarius::plot_dotplot(sobj, assay = "RNA",
                       column_name = "cell_type",
                       markers = markers,
                       nb_hline = 0) +
  ggplot2::scale_color_gradientn(colors = aquarius:::color_gene) +
  ggplot2::theme(legend.position = "right",
                 legend.box = "vertical",
                 legend.direction = "vertical",
                 axis.title = element_blank(),
                 axis.text = element_text(size = 15))

We can make a barplot to see the composition of each dataset, and visualize cell types on the projection.

df_proportion = as.data.frame(prop.table(table(sobj$orig.ident,
                                               sobj$cell_type)))
colnames(df_proportion) = c("orig.ident", "cell_type", "freq")

quantif = table(sobj$orig.ident) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("orig.ident", "nb_cells"))

# Plot
plot_list = list()

plot_list[[2]] = aquarius::plot_barplot(df = df_proportion,
                                        x = "orig.ident",
                                        y = "freq",
                                        fill = "cell_type",
                                        position = ggplot2::position_fill()) +
  ggplot2::scale_fill_manual(name = "Cell type",
                             values = color_markers[levels(df_proportion$cell_type)],
                             breaks = levels(df_proportion$cell_type)) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = orig.ident, y = 1.05, label = nb_cells),
                      label.size = 0)

plot_list[[1]] = Seurat::DimPlot(sobj, group.by = "cell_type") +
  ggplot2::scale_color_manual(values = unlist(color_markers),
                              breaks = names(color_markers)) +
  ggplot2::labs(title = sample_name,
                subtitle = paste0(ncol(sobj), " cells")) +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

patchwork::wrap_plots(plot_list, nrow = 1, widths = c(6, 1))

Cell cycle phase

We annotate cells for cell cycle phase using Seurat and cyclone.

cc_columns = aquarius::add_cell_cycle(sobj = sobj,
                                      assay = "RNA",
                                      species_rdx = "hs",
                                      BPPARAM = cl)@meta.data[, c("Seurat.Phase", "Phase")]
## 
##   G1  G2M    S 
## 1524  620 1467
sobj$Seurat.Phase = cc_columns$Seurat.Phase
sobj$cyclone.Phase = cc_columns$Phase

table(sobj$Seurat.Phase, sobj$cyclone.Phase)
##      
##         G1  G2M    S
##   G1  1007  291  928
##   G2M  202  270  101
##   S    315   59  438

(Time to run : 327.6 s)

We visualize cell cycle on the projection :

plot_list = list()

plot_list[[2]] = Seurat::DimPlot(sobj, group.by = "Seurat.Phase") +
  ggplot2::labs(title = "Cell Cycle Phase",
                subtitle = "Seurat.Phase") +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

plot_list[[1]] = Seurat::DimPlot(sobj, group.by = "cyclone.Phase") +
  ggplot2::labs(title = "Cell Cycle Phase",
                subtitle = "cyclone.Phase") +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

patchwork::wrap_plots(plot_list, nrow = 1)

Quality control

In this section, we look at the number of genes expressed by each cell, the number of UMI, the percentage of mitochondrial genes expressed, and the percentage of ribosomal genes expressed. Then, without taking into account the cells expressing low number of genes or have low number of UMI, we identify doublet cells.

We compute four quality metrics :

sobj = Seurat::PercentageFeatureSet(sobj, pattern = "^MT", col.name = "percent.mt")
sobj = Seurat::PercentageFeatureSet(sobj, pattern = "^RP[L|S][0-9]*$", col.name = "percent.rb")

head(sobj@meta.data)
##                    orig.ident nCount_RNA nFeature_RNA log_nCount_RNA
## AAACCCAAGGGAGGCA-1    2022_02        132          113       4.882802
## AAACCCACATCCTATT-1    2022_02       4408         1079       8.391176
## AAACCCAGTCGTCTCT-1    2022_02        145           98       4.976734
## AAACCCAGTGTAGGAC-1    2022_02      36306         5654      10.499738
## AAACGAACAACCAATC-1    2022_02       4347         1924       8.377241
## AAACGAACAAGTATCC-1    2022_02      17586         4102       9.774858
##                    project_name sample_identifier sample_type score_CD4_T_cells
## AAACCCAAGGGAGGCA-1      2022_02              HD_2          HD       -0.01751676
## AAACCCACATCCTATT-1      2022_02              HD_2          HD       -0.07039404
## AAACCCAGTCGTCTCT-1      2022_02              HD_2          HD       -0.01371429
## AAACCCAGTGTAGGAC-1      2022_02              HD_2          HD       -0.12999860
## AAACGAACAACCAATC-1      2022_02              HD_2          HD        0.04670195
## AAACGAACAAGTATCC-1      2022_02              HD_2          HD       -0.08027089
##                    score_CD8_T_cells score_Langerhans_cells score_macrophages
## AAACCCAAGGGAGGCA-1      -0.013660589            -0.02761229      -0.004617715
## AAACCCACATCCTATT-1      -0.052365793            -0.03726479      -0.019321763
## AAACCCAGTCGTCTCT-1      -0.006684504            -0.02789566       0.000000000
## AAACCCAGTGTAGGAC-1      -0.094085500            -0.07992263      -0.034099624
## AAACGAACAACCAATC-1      -0.120047718             1.30086554       0.280247068
## AAACGAACAAGTATCC-1      -0.084180891            -0.07394105       0.007045939
##                    score_B_cells score_cuticle score_cortex score_medulla
## AAACCCAAGGGAGGCA-1   0.000000000   -0.10869883  -0.02437153   -0.07331642
## AAACCCACATCCTATT-1  -0.004136588   -0.20339989   0.02566296   -0.20152377
## AAACCCAGTCGTCTCT-1   0.000000000   -0.05856797  -0.02923608   -0.04171171
## AAACCCAGTGTAGGAC-1  -0.006615494    0.55064212   0.03549632   -0.14114289
## AAACGAACAACCAATC-1  -0.016571413   -0.28599820  -0.15454661   -0.35797885
## AAACGAACAAGTATCC-1  -0.006659577   -0.33184117  -0.09560058   -0.27248035
##                       score_IRS score_proliferative   score_IBL   score_ORS
## AAACCCAAGGGAGGCA-1 -0.026075167         -0.03545802 -0.04777447  0.19477642
## AAACCCACATCCTATT-1 -0.085342508         -0.14121175  0.10708811  0.06384176
## AAACCCAGTCGTCTCT-1 -0.008720768         -0.02602588 -0.02111632 -0.05479933
## AAACCCAGTGTAGGAC-1 -0.061051817          0.68749458  0.01441245 -0.46298881
## AAACGAACAACCAATC-1 -0.179925686         -0.25546473 -0.24296155 -0.34843705
## AAACGAACAAGTATCC-1 -0.057478584         -0.19326806  0.28763750  0.16860190
##                      score_IFE  score_HFSC score_melanocytes score_sebocytes
## AAACCCAAGGGAGGCA-1 -0.01910979  0.17459906        0.38302816     -0.01640045
## AAACCCACATCCTATT-1  0.12074124 -0.19318666       -0.26786430     -0.06530349
## AAACCCAGTCGTCTCT-1 -0.01335850 -0.05727866       -0.07317189     -0.02675065
## AAACCCAGTGTAGGAC-1 -0.07085904 -0.33237672       -0.66517358     -0.05444636
## AAACGAACAACCAATC-1 -0.03453352 -0.22556268       -0.44869136      0.03282660
## AAACGAACAAGTATCC-1 -0.02356890  0.73492986       -0.33602596     -0.01420379
##                           cell_type Seurat.Phase cyclone.Phase percent.mt
## AAACCCAAGGGAGGCA-1      melanocytes            S            G1  2.2727273
## AAACCCACATCCTATT-1              IFE           G1             S 58.5299456
## AAACCCAGTCGTCTCT-1      macrophages           G1           G2M 34.4827586
## AAACCCAGTGTAGGAC-1    proliferative          G2M           G2M  5.2140142
## AAACGAACAACCAATC-1 Langerhans cells           G1             S  0.4600874
## AAACGAACAAGTATCC-1             HFSC            S             S  3.4004322
##                    percent.rb
## AAACCCAAGGGAGGCA-1  18.181818
## AAACCCACATCCTATT-1   1.202359
## AAACCCAGTCGTCTCT-1   2.068966
## AAACCCAGTGTAGGAC-1  24.285242
## AAACGAACAACCAATC-1   8.488613
## AAACGAACAAGTATCC-1  21.903787

We get the cell barcodes for the failing cells :

fail_percent.mt = sobj@meta.data %>% dplyr::filter(percent.mt > cut_percent.mt) %>% rownames()
fail_percent.rb = sobj@meta.data %>% dplyr::filter(percent.rb > cut_percent.rb) %>% rownames()
fail_log_nCount_RNA = sobj@meta.data %>% dplyr::filter(log_nCount_RNA < cut_log_nCount_RNA) %>% rownames()
fail_nFeature_RNA = sobj@meta.data %>% dplyr::filter(nFeature_RNA < cut_nFeature_RNA) %>% rownames()

Doublet cells detection

Without taking into account the low UMI and low number of features cells, we identify doublets.

fsobj = subset(sobj, invert = TRUE,
               cells = unique(c(fail_log_nCount_RNA, fail_nFeature_RNA)))
fsobj
## An object of class Seurat 
## 27955 features across 2858 samples within 1 assay 
## Active assay: RNA (27955 features, 3000 variable features)
##  2 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne

On this filtered dataset, we apply doublet cells detection. Just before, we run the normalization, taking into account only the remaining cells.

sobj = Seurat::NormalizeData(sobj,
                             normalization.method = "LogNormalize",
                             assay = "RNA")

sobj = Seurat::FindVariableFeatures(sobj,
                                    assay = "RNA",
                                    nfeatures = 3000)
sobj
## An object of class Seurat 
## 27955 features across 3612 samples within 1 assay 
## Active assay: RNA (27955 features, 3000 variable features)
##  2 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne

We identify doublet cells :

fsobj = aquarius::find_doublets(sobj = fsobj,
                                BPPARAM = cl)
## [1] 27955  2858
## 
## FALSE  TRUE 
##  2654   204 
## [17:24:45] WARNING: amalgamation/../src/learner.cc:1095: Starting in XGBoost 1.3.0, the default evaluation metric used with the objective 'binary:logistic' was changed from 'error' to 'logloss'. Explicitly set eval_metric if you'd like to restore the old behavior.
## 
## FALSE  TRUE 
##  2582   276 
## 
## FALSE  TRUE 
##  2463   395
fail_doublets_consensus = Seurat::WhichCells(fsobj, expression = doublets_consensus.class)
fail_doublets_scDblFinder = Seurat::WhichCells(fsobj, expression = scDblFinder.class)
fail_doublets_hybrid = Seurat::WhichCells(fsobj, expression = hybrid_score.class)

(Time to run : 92.99 s)

We add the information in the non filtered Seurat object :

sobj$doublets_consensus.class = dplyr::case_when(!(colnames(sobj) %in% colnames(fsobj)) ~ NA,
                                                 colnames(sobj) %in% fail_doublets_consensus ~ TRUE,
                                                 !(colnames(sobj) %in% fail_doublets_consensus) ~ FALSE)

sobj$scDblFinder.class = dplyr::case_when(!(colnames(sobj) %in% colnames(fsobj)) ~ NA,
                                          colnames(sobj) %in% fail_doublets_scDblFinder ~ TRUE,
                                          !(colnames(sobj) %in% fail_doublets_scDblFinder) ~ FALSE)

sobj$hybrid_score.class = dplyr::case_when(!(colnames(sobj) %in% colnames(fsobj)) ~ NA,
                                           colnames(sobj) %in% fail_doublets_hybrid ~ TRUE,
                                           !(colnames(sobj) %in% fail_doublets_hybrid) ~ FALSE)

Quality control representation

We can visualize the 4 cells quality with a Venn diagram :

n_filtered = c(fail_percent.mt, fail_percent.rb, fail_log_nCount_RNA, fail_nFeature_RNA) %>%
  unique() %>% length()
percent_filtered = round(100*(n_filtered/ncol(sobj)), 2)

ggvenn::ggvenn(list(percent.mt = fail_percent.mt,
                    percent.rb = fail_percent.rb,
                    log_nCount_RNA = fail_log_nCount_RNA,
                    nFeature_RNA = fail_nFeature_RNA), 
               fill_color = c("#0073C2FF", "#EFC000FF", "orange", "pink"),
               stroke_size = 0.5, set_name_size = 4) +
  ggplot2::labs(title = "Filtered out cells",
                subtitle = paste0(n_filtered, " cells (", percent_filtered, " % of all cells)")) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5))

Number of UMI

To visualize the threshold for number of UMI, we can make a histogram :

aquarius::plot_qc_density(df = sobj@meta.data,
                          x = "log_nCount_RNA",
                          bins = 200,
                          group_by = "orig.ident",
                          group_color = setNames(sample_info$color,
                                                 nm = sample_info$sample_identifiant),
                          x_thresh = cut_log_nCount_RNA)

Seurat::VlnPlot(sobj, features = "log_nCount_RNA", pt.size = 0.001,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::scale_fill_manual(values = color_markers, breaks = names(color_markers)) +
  ggplot2::geom_hline(yintercept = cut_log_nCount_RNA, col = "red") +
  ggplot2::labs(x = "")

sobj$fail = ifelse(colnames(sobj) %in% fail_log_nCount_RNA,
                   yes = as.character(sobj$cell_type), no = NA)
sobj$fail = factor(sobj$fail, levels = c(levels(sobj$cell_type), NA))

Seurat::DimPlot(sobj, group.by = "fail", na.value = "gray80", cols = color_markers) +
  ggplot2::labs(title = "log_nCount_RNA",
                subtitle = paste0(length(fail_log_nCount_RNA), " cells")) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

Number of features

To visualize the threshold for number of features, we can make a histogram :

aquarius::plot_qc_density(df = sobj@meta.data,
                          x = "nFeature_RNA",
                          bins = 200,
                          group_by = "orig.ident",
                          group_color = setNames(sample_info$color,
                                                 nm = sample_info$sample_identifiant),
                          x_thresh = cut_nFeature_RNA)

Seurat::VlnPlot(sobj, features = "nFeature_RNA", pt.size = 0.001,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::scale_fill_manual(values = color_markers, breaks = names(color_markers)) +
  ggplot2::geom_hline(yintercept = cut_nFeature_RNA, col = "red") +
  ggplot2::labs(x = "")

sobj$fail = ifelse(colnames(sobj) %in% fail_nFeature_RNA,
                   yes = as.character(sobj$cell_type), no = NA)
sobj$fail = factor(sobj$fail, levels = c(levels(sobj$cell_type), NA))

Seurat::DimPlot(sobj, group.by = "fail", na.value = "gray80", cols = color_markers) +
  ggplot2::labs(title = "nFeature_RNA",
                subtitle = paste0(length(fail_nFeature_RNA), " cells")) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

Mitochondrial genes expression

To identify a threshold for mitochondrial gene expression, we can make a histogram :

aquarius::plot_qc_density(df = sobj@meta.data,
                          x = "percent.mt",
                          bins = 200,
                          group_by = "orig.ident",
                          group_color = setNames(sample_info$color,
                                                 nm = sample_info$sample_identifiant),
                          x_thresh = cut_percent.mt)

Seurat::VlnPlot(sobj, features = "percent.mt", pt.size = 0.001,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::scale_fill_manual(values = color_markers, breaks = names(color_markers)) +
  ggplot2::geom_hline(yintercept = cut_percent.mt, col = "red") +
  ggplot2::labs(x = "")

sobj$fail = ifelse(colnames(sobj) %in% fail_percent.mt,
                   yes = as.character(sobj$cell_type), no = NA)
sobj$fail = factor(sobj$fail, levels = c(levels(sobj$cell_type), NA))

Seurat::DimPlot(sobj, group.by = "fail", na.value = "gray80", cols = color_markers) +
  ggplot2::labs(title = "percent.mt",
                subtitle = paste0(length(fail_percent.mt), " cells")) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

Ribosomal genes expression

To identify a threshold for ribosomal gene expression, we can make a histogram :

aquarius::plot_qc_density(df = sobj@meta.data,
                          x = "percent.rb",
                          bins = 200,
                          group_by = "orig.ident",
                          group_color = setNames(sample_info$color,
                                                 nm = sample_info$sample_identifiant),
                          x_thresh = cut_percent.rb)

Seurat::VlnPlot(sobj, features = "percent.rb", pt.size = 0.001,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::scale_fill_manual(values = color_markers, breaks = names(color_markers)) +
  ggplot2::geom_hline(yintercept = cut_percent.rb, col = "red") +
  ggplot2::labs(x = "")

sobj$fail = ifelse(colnames(sobj) %in% fail_percent.rb,
                   yes = as.character(sobj$cell_type), no = NA)
sobj$fail = factor(sobj$fail, levels = c(levels(sobj$cell_type), NA))

Seurat::DimPlot(sobj, group.by = "fail", na.value = "gray80", cols = color_markers) +
  ggplot2::labs(title = "percent.rb",
                subtitle = paste0(length(fail_percent.rb), " cells")) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

FACS-like figure

We would like to see if the number of feature expressed by cell, and the number of UMI is correlated with the cell type, the percentage of mitochondrial and ribosomal gene expressed, and the doublet status. We build the log_nCount_RNA by nFeature_RNA figure, where cells (dots) are colored by these different metrics.

This is the figure, colored by cell type :

aquarius::plot_qc_facslike(df = sobj@meta.data,
                           x = "nFeature_RNA",
                           y = "log_nCount_RNA",
                           col_by = "cell_type",
                           col_colors = unname(color_markers),
                           x_thresh = cut_nFeature_RNA,
                           y_thresh = cut_log_nCount_RNA,
                           bins = 200)

This is the figure, colored by the percentage of mitochondrial genes expressed in cell :

aquarius::plot_qc_facslike(df = sobj@meta.data,
                           x = "nFeature_RNA",
                           y = "log_nCount_RNA",
                           col_by = "percent.mt",
                           x_thresh = cut_nFeature_RNA,
                           y_thresh = cut_log_nCount_RNA,
                           bins = 200)

This is the figure, colored by the percentage of ribosomal genes expressed in cell :

aquarius::plot_qc_facslike(df = sobj@meta.data,
                           x = "nFeature_RNA",
                           y = "log_nCount_RNA",
                           col_by = "percent.rb",
                           x_thresh = cut_nFeature_RNA,
                           y_thresh = cut_log_nCount_RNA,
                           bins = 200)

This is the figure, colored by the doublet cells status (doublets_consensus.class) :

aquarius::plot_qc_facslike(df = sobj@meta.data,
                           x = "nFeature_RNA",
                           y = "log_nCount_RNA",
                           col_by = "doublets_consensus.class",
                           col_colors = setNames(nm = c(TRUE, FALSE),
                                                 aquarius::gg_color_hue(2)),
                           x_thresh = cut_nFeature_RNA,
                           y_thresh = cut_log_nCount_RNA,
                           bins = 200)

This is the figure, colored by the doublet cells status (scDblFinder.class) :

aquarius::plot_qc_facslike(df = sobj@meta.data,
                           x = "nFeature_RNA",
                           y = "log_nCount_RNA",
                           col_by = "scDblFinder.class",
                           col_colors = setNames(nm = c(TRUE, FALSE),
                                                 aquarius::gg_color_hue(2)),
                           x_thresh = cut_nFeature_RNA,
                           y_thresh = cut_log_nCount_RNA,
                           bins = 200)

This is the figure, colored by the doublet cells status (hybrid_score.class) :

aquarius::plot_qc_facslike(df = sobj@meta.data,
                           x = "nFeature_RNA",
                           y = "log_nCount_RNA",
                           col_by = "hybrid_score.class",
                           col_colors = setNames(nm = c(TRUE, FALSE),
                                                 aquarius::gg_color_hue(2)),
                           x_thresh = cut_nFeature_RNA,
                           y_thresh = cut_log_nCount_RNA,
                           bins = 200)

Visualization as piechart

Do filtered cells belong to a particular cell type ?

sobj$all_cells = TRUE

plot_list = list()

## All cells
df = sobj@meta.data
if (nrow(df) == 0) {
  plot_list[[1]] = ggplot()
} else {
  plot_list[[1]] = aquarius::plot_piechart(df = df,
                                           logical_var = "all_cells",
                                           grouping_var = "cell_type",
                                           colors = color_markers,
                                           display_legend = TRUE) +
    ggplot2::labs(title = "All cells",
                  subtitle = paste(nrow(df), "cells")) +
    ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                   plot.subtitle = element_text(hjust = 0.5))
}

## Doublets consensus
df = sobj@meta.data %>%
  dplyr::filter(doublets_consensus.class)
if (nrow(df) == 0) {
  plot_list[[2]] = ggplot()
} else {
  plot_list[[2]] = aquarius::plot_piechart(df = df,
                                           logical_var = "all_cells",
                                           grouping_var = "cell_type",
                                           colors = color_markers,
                                           display_legend = TRUE) +
    ggplot2::labs(title = "doublets_consensus.class",
                  subtitle = paste(sum(sobj$doublets_consensus.class, na.rm = TRUE), "cells")) +
    ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                   plot.subtitle = element_text(hjust = 0.5))
}

## percent.mt
df = sobj@meta.data %>%
  dplyr::filter(percent.mt > cut_percent.mt)
if (nrow(df) == 0) {
  plot_list[[3]] = ggplot()
} else {
  plot_list[[3]] = aquarius::plot_piechart(df = df,
                                           logical_var = "all_cells",
                                           grouping_var = "cell_type",
                                           colors = color_markers,
                                           display_legend = TRUE) +
    ggplot2::labs(title = paste("percent.mt >", cut_percent.mt),
                  subtitle = paste(length(fail_percent.mt), "cells")) +
    ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                   plot.subtitle = element_text(hjust = 0.5))
}

## percent.rb
df = sobj@meta.data %>%
  dplyr::filter(percent.rb > cut_percent.rb)
if (nrow(df) == 0) {
  plot_list[[4]] = ggplot()
} else {
  plot_list[[4]] = aquarius::plot_piechart(df = df,
                                           logical_var = "all_cells",
                                           grouping_var = "cell_type",
                                           colors = color_markers,
                                           display_legend = TRUE) +
    ggplot2::labs(title = paste("percent.rb >", cut_percent.rb),
                  subtitle = paste(length(fail_percent.rb), "cells")) +
    ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                   plot.subtitle = element_text(hjust = 0.5))
}

## log_nCount_RNA
df = sobj@meta.data %>%
  dplyr::filter(log_nCount_RNA < cut_log_nCount_RNA)
if (nrow(df) == 0) {
  plot_list[[5]] = ggplot()
} else {
  plot_list[[5]] = aquarius::plot_piechart(df = df,
                                           logical_var = "all_cells",
                                           grouping_var = "cell_type",
                                           colors = color_markers,
                                           display_legend = TRUE) +
    ggplot2::labs(title = paste("log_nCount_RNA <", round(cut_log_nCount_RNA, 2)),
                  subtitle = paste(length(fail_log_nCount_RNA), "cells")) +
    ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                   plot.subtitle = element_text(hjust = 0.5))
}

## nFeature_RNA
df = sobj@meta.data %>%
  dplyr::filter(nFeature_RNA < cut_nFeature_RNA)
if (nrow(df) == 0) {
  plot_list[[6]] = ggplot()
} else {
  plot_list[[6]] = aquarius::plot_piechart(df = df,
                                           logical_var = "all_cells",
                                           grouping_var = "cell_type",
                                           colors = color_markers,
                                           display_legend = TRUE) +
    ggplot2::labs(title = paste("nFeature_RNA <", round(cut_nFeature_RNA, 2)),
                  subtitle = paste(length(fail_nFeature_RNA), "cells")) +
    ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"),
                   plot.subtitle = element_text(hjust = 0.5))
}

patchwork::wrap_plots(plot_list, ncol = 3) +
  patchwork::plot_layout(guides = "collect") &
  ggplot2::theme(legend.position = "right")

Doublet cells status

We can compare doublet detection methods with a Venn diagram :

ggvenn::ggvenn(list(hybrid = fail_doublets_hybrid,
                    scDblFinder = fail_doublets_scDblFinder), 
               fill_color = c("#0073C2FF", "#EFC000FF"),
               stroke_size = 0.5, set_name_size = 4) +
  ggplot2::ggtitle(label = "Doublet cells") +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold"))

We visualize cells annotation for doublets :

plot_list = list()

# scDblFinder.class
sobj$fail = ifelse(sobj$scDblFinder.class,
                   yes = as.character(sobj$cell_type), no = NA)
sobj$fail = factor(sobj$fail, levels = c(levels(sobj$cell_type), NA))

plot_list[[1]] = Seurat::DimPlot(sobj, group.by = "fail",
                                 na.value = "gray80", cols = color_markers) +
  ggplot2::labs(title = "scDblFinder.class",
                subtitle = paste0(sum(sobj$scDblFinder.class, na.rm = TRUE), " cells")) +
  Seurat::NoAxes() + Seurat::NoLegend() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

# hybrid_score.class
sobj$fail = ifelse(sobj$hybrid_score.class,
                   yes = as.character(sobj$cell_type), no = NA)
sobj$fail = factor(sobj$fail, levels = c(levels(sobj$cell_type), NA))

plot_list[[2]] = Seurat::DimPlot(sobj, group.by = "fail",
                                 na.value = "gray80", cols = color_markers) +
  ggplot2::labs(title = "hybrid_score.class",
                subtitle = paste0(sum(sobj$hybrid_score.class, na.rm = TRUE), " cells")) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

sobj$fail = NULL

# Plot
patchwork::wrap_plots(plot_list, nrow = 1)

What is the composition of doublet cells ? We just look at score for each cell type.

sobj$orig.ident.doublets = case_when(is.na(sobj$doublets_consensus.class) ~ "bad quality",
                                     sobj$doublets_consensus.class == TRUE ~ paste0(sobj$orig.ident, " doublets"),
                                     sobj$doublets_consensus.class == FALSE ~ "not doublet")
sobj$orig.ident.doublets = factor(sobj$orig.ident.doublets,
                                  levels = c(paste0(as.character(sample_info$sample_identifiant), " doublets"),
                                             "bad quality", "not doublet"))

doublets_compo = function(score1, score2) {
  type1 = unlist(lapply(stringr::str_split(score1, pattern = "score_"), `[[`, 2))
  type2 = unlist(lapply(stringr::str_split(score2, pattern = "score_"), `[[`, 2))
  
  if (type1 == type2) {
    the_title = "Homotypic doublet"
    the_subtitle = type1
    score1 = "log_nCount_RNA"
  } else {
    the_title = "Heterotypic doublet"
    the_subtitle = paste(type1, type2, sep = " + ")
  }
  
  p = sobj@meta.data %>%
    dplyr::arrange(desc(orig.ident.doublets)) %>%
    ggplot2::ggplot(., aes(x = eval(parse(text = score1)),
                           y = eval(parse(text = score2)),
                           col = orig.ident.doublets)) +
    ggplot2::geom_point(size = 0.25) +
    ggplot2::scale_color_manual(values = c(sample_info$color, "gray90", "gray60"),
                                breaks = c(paste0(as.character(sample_info$sample_identifiant), " doublets"),
                                           "bad quality", "not doublet")) +
    ggplot2::labs(x = score1, y = score2,
                  title = the_title, subtitle = the_subtitle) +
    ggplot2::theme_classic() +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5),
                   plot.subtitle = element_text(hjust = 0.5))
  
  return(p)
}
score_columns = grep(x = colnames(sobj@meta.data),
                     pattern = "^score",
                     value = TRUE)
combinations = expand.grid(score_columns, score_columns) %>%
  apply(., 1, sort) %>% t() %>%
  as.data.frame()
combinations = combinations[!duplicated(combinations), ]

plot_list = apply(combinations, 1, FUN = function(elem) {
  doublets_compo(elem[1], elem[2])
})

sobj$orig.ident.doublets = NULL
patchwork::wrap_plots(plot_list, ncol = 4) +
  patchwork::plot_layout(guides = "collect") &
  ggplot2::theme(legend.position = "right")
show

Save

We could save this object before filtering (remove eval = FALSE) :

saveRDS(sobj, paste0(out_dir, "/datasets/", sample_name, "_sobj_unfiltered.rds"))

Filtering

We remove :

  • cells with a number of UMI lower than 6
  • cells expressing a number of genes lower than 500
  • cells having more than 20 % of UMI related to mitochondrial genes
  • cells having more than 50 % of UMI related to ribosomal genes
  • doublet cells detected with both method (scDblFinder and scds-hybrid)
sobj = subset(sobj, invert = TRUE,
              cells = unique(c(fail_log_nCount_RNA, fail_nFeature_RNA,
                               fail_percent.mt, fail_percent.rb,
                               fail_doublets_consensus)))
sobj
## An object of class Seurat 
## 27955 features across 2286 samples within 1 assay 
## Active assay: RNA (27955 features, 3000 variable features)
##  2 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne

Post-filtering processing

Normalization

We normalize the count matrix for remaining cells :

sobj = Seurat::NormalizeData(sobj,
                             normalization.method = "LogNormalize",
                             assay = "RNA")

sobj = Seurat::FindVariableFeatures(sobj,
                                    assay = "RNA",
                                    nfeatures = 3000)
sobj
## An object of class Seurat 
## 27955 features across 2286 samples within 1 assay 
## Active assay: RNA (27955 features, 3000 variable features)
##  2 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne

Projection

We perform a PCA :

sobj = aquarius::dimensions_reduction(sobj = sobj,
                                      assay = "RNA",
                                      reduction = "pca",
                                      max_dims = 100,
                                      verbose = FALSE)
Seurat::ElbowPlot(sobj, ndims = 100, reduction = "RNA_pca")

We generate a tSNE and a UMAP with 20 principal components :

ndims = 20
sobj = Seurat::RunTSNE(sobj,
                       reduction = "RNA_pca",
                       dims = 1:ndims,
                       seed.use = 1337L,
                       reduction.name = paste0("RNA_pca_", ndims, "_tsne"))

sobj = Seurat::RunUMAP(sobj,
                       reduction = "RNA_pca",
                       dims = 1:ndims,
                       seed.use = 1337L,
                       reduction.name = paste0("RNA_pca_", ndims, "_umap"))

Annotation

We annotate cells for cell type, with the new normalized expression matrix :

score_columns = grep(x = colnames(sobj@meta.data), pattern = "^score", value = TRUE)
sobj@meta.data[, score_columns] = NULL
sobj$cell_type = NULL

sobj = aquarius::cell_annot_custom(sobj,
                                   newname = "cell_type",
                                   markers = cell_markers,
                                   use_negative = TRUE,
                                   add_score = TRUE,
                                   verbose = TRUE)

sobj$cell_type = factor(sobj$cell_type, levels = names(cell_markers))

colnames(sobj@meta.data) = stringr::str_replace_all(string = colnames(sobj@meta.data),
                                                    pattern = " ",
                                                    replacement = "_")

table(sobj$cell_type)
## 
##      CD4 T cells      CD8 T cells Langerhans cells      macrophages 
##              119               65               32               29 
##          B cells          cuticle           cortex          medulla 
##               21              305               59              120 
##              IRS    proliferative              IBL              ORS 
##              103              466              134              272 
##              IFE             HFSC      melanocytes        sebocytes 
##               48              214              276               23

(Time to run : 18.41 s)

To justify cell type annotation, we can make a dotplot :

markers = c("PTPRC", unique(unlist(dotplot_markers[levels(sobj$cell_type)])))
markers = markers[markers %in% rownames(sobj)]

aquarius::plot_dotplot(sobj, assay = "RNA",
                       column_name = "cell_type",
                       markers = markers,
                       nb_hline = 0) +
  ggplot2::scale_color_gradientn(colors = aquarius:::color_gene) +
  ggplot2::theme(legend.position = "right",
                 legend.box = "vertical",
                 legend.direction = "vertical",
                 axis.title = element_blank(),
                 axis.text = element_text(size = 15))

We can make a barplot to see the composition of each dataset, and visualize cell types on the projection.

df_proportion = as.data.frame(prop.table(table(sobj$orig.ident,
                                               sobj$cell_type)))
colnames(df_proportion) = c("orig.ident", "cell_type", "freq")

quantif = table(sobj$orig.ident) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("orig.ident", "nb_cells"))

# Plot
plot_list = list()

plot_list[[2]] = aquarius::plot_barplot(df = df_proportion,
                                        x = "orig.ident",
                                        y = "freq",
                                        fill = "cell_type",
                                        position = ggplot2::position_fill()) +
  ggplot2::scale_fill_manual(name = "Cell type",
                             values = color_markers[levels(df_proportion$cell_type)],
                             breaks = levels(df_proportion$cell_type)) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = orig.ident, y = 1.05, label = nb_cells),
                      label.size = 0)

plot_list[[1]] = Seurat::DimPlot(sobj, group.by = "cell_type",
                                 reduction = "RNA_pca_20_tsne") +
  ggplot2::scale_color_manual(values = unlist(color_markers),
                              breaks = names(color_markers)) +
  ggplot2::labs(title = sample_name,
                subtitle = paste0(ncol(sobj), " cells")) +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

patchwork::wrap_plots(plot_list, nrow = 1, widths = c(6, 1))

Cell cycle

We annotate cells for cell cycle phase :

cc_columns = aquarius::add_cell_cycle(sobj = sobj,
                                      assay = "RNA",
                                      species_rdx = "hs",
                                      BPPARAM = cl)@meta.data[, c("Seurat.Phase", "Phase")]
## 
##   G1  G2M    S 
## 1083  258  945
sobj$Seurat.Phase = cc_columns$Seurat.Phase
sobj$cyclone.Phase = cc_columns$Phase

table(sobj$Seurat.Phase, sobj$cyclone.Phase)
##      
##        G1 G2M   S
##   G1  764 112 632
##   G2M 107 130  41
##   S   212  16 272

(Time to run : 240.75 s)

We visualize cell cycle on the projection :

plot_list = list()

plot_list[[2]] = Seurat::DimPlot(sobj, group.by = "Seurat.Phase",
                                 reduction = "RNA_pca_20_tsne") +
  ggplot2::labs(title = "Cell Cycle Phase",
                subtitle = "Seurat.Phase") +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

plot_list[[1]] = Seurat::DimPlot(sobj, group.by = "cyclone.Phase",
                                 reduction = "RNA_pca_20_tsne") +
  ggplot2::labs(title = "Cell Cycle Phase",
                subtitle = "cyclone.Phase") +
  Seurat::NoLegend() + Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 plot.subtitle = element_text(hjust = 0.5))

patchwork::wrap_plots(plot_list, nrow = 1)

Clustering

We make a highly resolutive clustering :

sobj = Seurat::FindNeighbors(sobj, reduction = "RNA_pca", dims = c(1:ndims))
sobj = Seurat::FindClusters(sobj, resolution = 2)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 2286
## Number of edges: 67216
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8252
## Number of communities: 22
## Elapsed time: 0 seconds
table(sobj$seurat_clusters)
## 
##   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19 
## 193 170 167 158 149 144 142 138 129 121 111 102  98  92  89  53  49  48  44  31 
##  20  21 
##  30  28

Visualization

Cell type

We can visualize the cell type :

tsne = Seurat::DimPlot(sobj, group.by = "cell_type",
                       reduction = paste0("RNA_pca_", ndims, "_tsne"), cols = color_markers) +
  Seurat::NoAxes() + ggplot2::ggtitle("tSNE") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 legend.position = "none")

umap = Seurat::DimPlot(sobj, group.by = "cell_type",
                       reduction = paste0("RNA_pca_", ndims, "_umap"), cols = color_markers) +
  Seurat::NoAxes() + ggplot2::ggtitle("UMAP") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

tsne | umap

Cell cycle

We can visualize the cell cycle, from Seurat :

tsne = Seurat::DimPlot(sobj, group.by = "Seurat.Phase",
                       reduction = paste0("RNA_pca_", ndims, "_tsne")) +
  Seurat::NoAxes() + ggplot2::ggtitle("tSNE") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 legend.position = "none")

umap = Seurat::DimPlot(sobj, group.by = "Seurat.Phase",
                       reduction = paste0("RNA_pca_", ndims, "_umap")) +
  Seurat::NoAxes() + ggplot2::ggtitle("UMAP") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

tsne | umap

We can visualize the cell cycle, from cyclone :

tsne = Seurat::DimPlot(sobj, group.by = "cyclone.Phase",
                       reduction = paste0("RNA_pca_", ndims, "_tsne")) +
  Seurat::NoAxes() + ggplot2::ggtitle("tSNE") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 legend.position = "none")

umap = Seurat::DimPlot(sobj, group.by = "cyclone.Phase",
                       reduction = paste0("RNA_pca_", ndims, "_umap")) +
  Seurat::NoAxes() + ggplot2::ggtitle("UMAP") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

tsne | umap

Clusters

We visualize the clustering :

tsne = Seurat::DimPlot(sobj, group.by = "seurat_clusters", label = TRUE,
                       reduction = paste0("RNA_pca_", ndims, "_tsne")) +
  Seurat::NoAxes() + ggplot2::ggtitle("tSNE") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5),
                 legend.position = "none")

umap = Seurat::DimPlot(sobj, group.by = "seurat_clusters", label = TRUE,
                       reduction = paste0("RNA_pca_", ndims, "_umap")) +
  Seurat::NoAxes() + ggplot2::ggtitle("UMAP") +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

tsne | umap

Gene expression

We visualize all cell types markers on the tSNE :

markers = dotplot_markers %>% unlist() %>% unname()
markers = markers[markers %in% rownames(sobj)]

plot_list = lapply(markers,
                   FUN = function(one_gene) {
                     p = Seurat::FeaturePlot(sobj, features = one_gene,
                                             reduction = paste0("RNA_pca_", ndims, "_tsne")) +
                       ggplot2::labs(title = one_gene) +
                       ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
                       ggplot2::theme(aspect.ratio = 1,
                                      plot.subtitle = element_text(hjust = 0.5)) +
                       Seurat::NoAxes()
                     return(p)
                   })

patchwork::wrap_plots(plot_list, ncol = 4)

Save

We save the annotated and filtered Seurat object :

saveRDS(sobj, file = paste0(out_dir, "/datasets/", sample_name, "_sobj_filtered.rds"))

R session

show
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so
## LAPACK: /usr/local/lib/R/lib/libRlapack.so
## 
## locale:
## [1] C
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] ggplot2_3.3.5   patchwork_1.1.2 dplyr_1.0.7    
## 
## loaded via a namespace (and not attached):
##   [1] softImpute_1.4              graphlayouts_0.7.0         
##   [3] pbapply_1.4-2               lattice_0.20-41            
##   [5] haven_2.3.1                 vctrs_0.3.8                
##   [7] usethis_2.0.1               dynwrap_1.2.1              
##   [9] blob_1.2.1                  survival_3.2-13            
##  [11] prodlim_2019.11.13          dynutils_1.0.5             
##  [13] later_1.3.0                 DBI_1.1.1                  
##  [15] R.utils_2.11.0              SingleCellExperiment_1.8.0 
##  [17] rappdirs_0.3.3              uwot_0.1.8                 
##  [19] dqrng_0.2.1                 jpeg_0.1-8.1               
##  [21] zlibbioc_1.32.0             pspline_1.0-18             
##  [23] pcaMethods_1.78.0           mvtnorm_1.1-1              
##  [25] htmlwidgets_1.5.4           GlobalOptions_0.1.2        
##  [27] future_1.22.1               UpSetR_1.4.0               
##  [29] laeken_0.5.2                leiden_0.3.3               
##  [31] clustree_0.4.3              parallel_3.6.3             
##  [33] scater_1.14.6               irlba_2.3.3                
##  [35] DEoptimR_1.0-9              tidygraph_1.1.2            
##  [37] Rcpp_1.0.9                  readr_2.0.2                
##  [39] KernSmooth_2.23-17          carrier_0.1.0              
##  [41] promises_1.1.0              gdata_2.18.0               
##  [43] DelayedArray_0.12.3         limma_3.42.2               
##  [45] graph_1.64.0                RcppParallel_5.1.4         
##  [47] Hmisc_4.4-0                 fs_1.5.2                   
##  [49] RSpectra_0.16-0             fastmatch_1.1-0            
##  [51] ranger_0.12.1               digest_0.6.25              
##  [53] png_0.1-7                   sctransform_0.2.1          
##  [55] cowplot_1.0.0               DOSE_3.12.0                
##  [57] ggvenn_0.1.9                here_1.0.1                 
##  [59] TInGa_0.0.0.9000            ggraph_2.0.3               
##  [61] pkgconfig_2.0.3             GO.db_3.10.0               
##  [63] DelayedMatrixStats_1.8.0    gower_0.2.1                
##  [65] ggbeeswarm_0.6.0            iterators_1.0.12           
##  [67] DropletUtils_1.6.1          reticulate_1.26            
##  [69] clusterProfiler_3.14.3      SummarizedExperiment_1.16.1
##  [71] circlize_0.4.15             beeswarm_0.4.0             
##  [73] GetoptLong_1.0.5            xfun_0.35                  
##  [75] bslib_0.3.1                 zoo_1.8-10                 
##  [77] tidyselect_1.1.0            reshape2_1.4.4             
##  [79] purrr_0.3.4                 ica_1.0-2                  
##  [81] pcaPP_1.9-73                viridisLite_0.3.0          
##  [83] rtracklayer_1.46.0          rlang_1.0.2                
##  [85] hexbin_1.28.1               jquerylib_0.1.4            
##  [87] dyneval_0.9.9               glue_1.4.2                 
##  [89] RColorBrewer_1.1-2          matrixStats_0.56.0         
##  [91] stringr_1.4.0               lava_1.6.7                 
##  [93] europepmc_0.3               DESeq2_1.26.0              
##  [95] recipes_0.1.17              labeling_0.3               
##  [97] httpuv_1.5.2                class_7.3-17               
##  [99] BiocNeighbors_1.4.2         DO.db_2.9                  
## [101] annotate_1.64.0             jsonlite_1.7.2             
## [103] XVector_0.26.0              bit_4.0.4                  
## [105] mime_0.9                    aquarius_0.1.5             
## [107] Rsamtools_2.2.3             gridExtra_2.3              
## [109] gplots_3.0.3                stringi_1.4.6              
## [111] processx_3.5.2              gsl_2.1-6                  
## [113] bitops_1.0-6                cli_3.0.1                  
## [115] batchelor_1.2.4             RSQLite_2.2.0              
## [117] randomForest_4.6-14         tidyr_1.1.4                
## [119] data.table_1.14.2           rstudioapi_0.13            
## [121] org.Mm.eg.db_3.10.0         GenomicAlignments_1.22.1   
## [123] nlme_3.1-147                qvalue_2.18.0              
## [125] scran_1.14.6                locfit_1.5-9.4             
## [127] scDblFinder_1.1.8           listenv_0.8.0              
## [129] ggthemes_4.2.4              gridGraphics_0.5-0         
## [131] R.oo_1.24.0                 dbplyr_1.4.4               
## [133] BiocGenerics_0.32.0         TTR_0.24.2                 
## [135] readxl_1.3.1                lifecycle_1.0.1            
## [137] timeDate_3043.102           ggpattern_0.3.1            
## [139] munsell_0.5.0               cellranger_1.1.0           
## [141] R.methodsS3_1.8.1           proxyC_0.1.5               
## [143] visNetwork_2.0.9            caTools_1.18.0             
## [145] codetools_0.2-16            Biobase_2.46.0             
## [147] GenomeInfoDb_1.22.1         vipor_0.4.5                
## [149] lmtest_0.9-38               msigdbr_7.5.1              
## [151] htmlTable_1.13.3            triebeard_0.3.0            
## [153] lsei_1.2-0                  xtable_1.8-4               
## [155] ROCR_1.0-7                  BiocManager_1.30.10        
## [157] scatterplot3d_0.3-41        abind_1.4-5                
## [159] farver_2.0.3                parallelly_1.28.1          
## [161] RANN_2.6.1                  askpass_1.1                
## [163] GenomicRanges_1.38.0        RcppAnnoy_0.0.16           
## [165] tibble_3.1.5                ggdendro_0.1-20            
## [167] cluster_2.1.0               future.apply_1.5.0         
## [169] Seurat_3.1.5                dendextend_1.15.1          
## [171] Matrix_1.3-2                ellipsis_0.3.2             
## [173] prettyunits_1.1.1           lubridate_1.7.9            
## [175] ggridges_0.5.2              igraph_1.2.5               
## [177] RcppEigen_0.3.3.7.0         fgsea_1.12.0               
## [179] remotes_2.4.2               scBFA_1.0.0                
## [181] destiny_3.0.1               VIM_6.1.1                  
## [183] testthat_3.1.0              htmltools_0.5.2            
## [185] BiocFileCache_1.10.2        yaml_2.2.1                 
## [187] utf8_1.1.4                  plotly_4.9.2.1             
## [189] XML_3.99-0.3                ModelMetrics_1.2.2.2       
## [191] e1071_1.7-3                 foreign_0.8-76             
## [193] withr_2.5.0                 fitdistrplus_1.0-14        
## [195] BiocParallel_1.20.1         xgboost_1.4.1.1            
## [197] bit64_4.0.5                 foreach_1.5.0              
## [199] robustbase_0.93-9           Biostrings_2.54.0          
## [201] GOSemSim_2.13.1             rsvd_1.0.3                 
## [203] memoise_2.0.0               evaluate_0.18              
## [205] forcats_0.5.0               rio_0.5.16                 
## [207] geneplotter_1.64.0          tzdb_0.1.2                 
## [209] caret_6.0-86                ps_1.6.0                   
## [211] DiagrammeR_1.0.6.1          curl_4.3                   
## [213] fdrtool_1.2.15              fansi_0.4.1                
## [215] highr_0.8                   urltools_1.7.3             
## [217] xts_0.12.1                  GSEABase_1.48.0            
## [219] acepack_1.4.1               edgeR_3.28.1               
## [221] checkmate_2.0.0             scds_1.2.0                 
## [223] cachem_1.0.6                npsurv_0.4-0               
## [225] babelgene_22.3              rjson_0.2.20               
## [227] openxlsx_4.1.5              ggrepel_0.9.1              
## [229] clue_0.3-60                 rprojroot_2.0.2            
## [231] stabledist_0.7-1            tools_3.6.3                
## [233] sass_0.4.0                  nichenetr_1.1.1            
## [235] magrittr_2.0.1              RCurl_1.98-1.2             
## [237] proxy_0.4-24                car_3.0-11                 
## [239] ape_5.3                     ggplotify_0.0.5            
## [241] xml2_1.3.2                  httr_1.4.2                 
## [243] assertthat_0.2.1            rmarkdown_2.18             
## [245] boot_1.3-25                 globals_0.14.0             
## [247] R6_2.4.1                    Rhdf5lib_1.8.0             
## [249] nnet_7.3-14                 RcppHNSW_0.2.0             
## [251] progress_1.2.2              genefilter_1.68.0          
## [253] statmod_1.4.34              gtools_3.8.2               
## [255] shape_1.4.6                 HDF5Array_1.14.4           
## [257] BiocSingular_1.2.2          rhdf5_2.30.1               
## [259] splines_3.6.3               AUCell_1.8.0               
## [261] carData_3.0-4               colorspace_1.4-1           
## [263] generics_0.1.0              stats4_3.6.3               
## [265] base64enc_0.1-3             dynfeature_1.0.0           
## [267] smoother_1.1                gridtext_0.1.1             
## [269] pillar_1.6.3                tweenr_1.0.1               
## [271] sp_1.4-1                    ggplot.multistats_1.0.0    
## [273] rvcheck_0.1.8               GenomeInfoDbData_1.2.2     
## [275] plyr_1.8.6                  gtable_0.3.0               
## [277] zip_2.2.0                   knitr_1.41                 
## [279] ComplexHeatmap_2.14.0       latticeExtra_0.6-29        
## [281] biomaRt_2.42.1              IRanges_2.20.2             
## [283] fastmap_1.1.0               ADGofTest_0.3              
## [285] copula_1.0-0                doParallel_1.0.15          
## [287] AnnotationDbi_1.48.0        vcd_1.4-8                  
## [289] babelwhale_1.0.1            openssl_1.4.1              
## [291] scales_1.1.1                backports_1.2.1            
## [293] S4Vectors_0.24.4            ipred_0.9-12               
## [295] enrichplot_1.6.1            hms_1.1.1                  
## [297] ggforce_0.3.1               Rtsne_0.15                 
## [299] shiny_1.7.1                 numDeriv_2016.8-1.1        
## [301] polyclip_1.10-0             grid_3.6.3                 
## [303] lazyeval_0.2.2              Formula_1.2-3              
## [305] tsne_0.1-3                  crayon_1.3.4               
## [307] MASS_7.3-54                 pROC_1.16.2                
## [309] viridis_0.5.1               dynparam_1.0.0             
## [311] rpart_4.1-15                zinbwave_1.8.0             
## [313] compiler_3.6.3              ggtext_0.1.0
LS0tCnBhcmFtczoKICBzYW1wbGVfbmFtZTogIjIwMjFfMzEiCnRpdGxlOiAiSFMgcHJvamVjdCIKc3VidGl0bGU6ICJTYW1wbGUgYHIgcGFyYW1zJHNhbXBsZV9uYW1lYCIKYXV0aG9yOiAiQXVkcmV5IgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclWS0lbS0lZCcpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCi0tLQoKPHN0eWxlPgpib2R5IHsKdGV4dC1hbGlnbjoganVzdGlmeX0KPC9zdHlsZT4KCjwhLS0gQXV0b21hdGljYWxseSBjb21wdXRlcyBhbmQgcHJpbnRzIGluIHRoZSBvdXRwdXQgdGhlIHJ1bm5pbmcgdGltZSBmb3IgYW55IGNvZGUgY2h1bmsgLS0+CmBgYHtyLCBlY2hvPUZBTFNFfQojIGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL3JtYXJrZG93bi9pc3N1ZXMvMTQ1Mwpob29rcyA9IGtuaXRyOjprbml0X2hvb2tzJGdldCgpCmhvb2tfZm9sZGFibGUgPSBmdW5jdGlvbih0eXBlKSB7CiAgZm9yY2UodHlwZSkKICBmdW5jdGlvbih4LCBvcHRpb25zKSB7CiAgICByZXMgPSBob29rc1tbdHlwZV1dKHgsIG9wdGlvbnMpCiAgICAKICAgIGlmIChpc0ZBTFNFKG9wdGlvbnNbW3Bhc3RlMCgiZm9sZF8iLCB0eXBlKV1dKSkgcmV0dXJuKHJlcykKICAgIAogICAgcGFzdGUwKAogICAgICAiPGRldGFpbHM+PHN1bW1hcnk+IiwgInNob3ciLCAiPC9zdW1tYXJ5PlxuXG4iLAogICAgICByZXMsCiAgICAgICJcblxuPC9kZXRhaWxzPiIKICAgICkKICB9Cn0Ka25pdHI6OmtuaXRfaG9va3Mkc2V0KAogIG91dHB1dCA9IGhvb2tfZm9sZGFibGUoIm91dHB1dCIpLAogIHBsb3QgPSBob29rX2ZvbGRhYmxlKCJwbG90IiksCiAgdGltZV9pdCA9IGxvY2FsKHsKICAgIG5vdyA9IE5VTEwKICAgIGZ1bmN0aW9uKGJlZm9yZSwgb3B0aW9ucykgewogICAgICBpZiAob3B0aW9ucyR0aW1lX2l0KSB7CiAgICAgICAgaWYgKGJlZm9yZSkgewogICAgICAgICAgbm93IDw8LSBTeXMudGltZSgpCiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIHJlcyA9IGRpZmZ0aW1lKFN5cy50aW1lKCksIG5vdywgdW5pdHMgPSAic2VjcyIpCiAgICAgICAgICBwYXN0ZSgiKFRpbWUgdG8gcnVuIDoiLCByb3VuZChyZXMsIGRpZ2l0cyA9IDIpLCAicykiKQogICAgICAgIH0KICAgICAgfQogICAgfQogIH0pCikKYGBgCgo8IS0tIFNldCBkZWZhdWx0IHBhcmFtZXRlcnMgZm9yIGFsbCBjaHVua3MgLS0+CmBgYHtyLCBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQpzZXQuc2VlZCgxMzM3TCkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAjIGRpc3BsYXkgY29kZQogICAgICAgICAgICAgICAgICAgICAgIyBkaXNwbGF5IGNodW5rIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZm9sZF9vdXRwdXQgPSBGQUxTRSwgIyB1c2VmdWxsIGZvciBzZXNzaW9uSW5mbygpCiAgICAgICAgICAgICAgICAgICAgICBmb2xkX3Bsb3QgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBmaWd1cmUgc2V0dGluZ3MKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdjZW50ZXInLAogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gMjAsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gMTUsCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgc29tZXRoaW5nIGFib3V0IHNlZWQsIGNodW5rIGFuZCBSbWFya2Rvd24gY29tcGlsYXRpb24KICAgICAgICAgICAgICAgICAgICAgICMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzk0MTcwMDMvbG9uZy12ZWN0b3JzLW5vdC1zdXBwb3J0ZWQteWV0LWVycm9yLWluLXJtZC1idXQtbm90LWluLXItc2NyaXB0CiAgICAgICAgICAgICAgICAgICAgICAjIGNhY2hlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGNhY2hlLmxhenkgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgYWRkIHJ1bnRpbWUgYWZ0ZXIgY2h1bmsKICAgICAgICAgICAgICAgICAgICAgIHRpbWVfaXQgPSBGQUxTRSkKYGBgCgoKVGhlIGdvYWwgb2YgdGhpcyBzY3JpcHQgaXMgdG8gZ2VuZXJhdGUgYSBTZXVyYXQgb2JqZWN0IGZvciBzYW1wbGUgYHIgcGFyYW1zJHNhbXBsZV9uYW1lYC4KCiogcmVtb3ZhbCBvZiBjZWxscyBiYXNlZCBvbiBxdWFsaXR5IGNvbnRyb2wgbWV0cmljcwoqIG5vcm1hbGl6YXRpb24gd2l0aCBgTG9nTm9ybWFsaXplYCwgdGhlbiBkb3VibGV0cyBkZXRlY3Rpb24gdXNpbmcgYHNjcmFuIGh5YnJpZGAgYW5kIGBzY0RibEZpbmRlcmAgbWV0aG9kLCBhbmQgZG91YmxldCBjZWxscyByZW1vdmFsCiogbm9ybWFsaXphdGlvbiB3aXRoIGBMb2dOb3JtYWxpemVgLCBmb3Igb25seSB0aGUgcmVtYWluaW5nIGNlbGxzCiogY2VsbCBjeWNsZSBhbmQgY2VsbCB0eXBlIGFubm90YXRpb24KKiBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gdXNpbmcgYFBDQWAKKiBwcm9qZWN0aW9uIHVzaW5nIGB0U05FYCBhbmQgYFVNQVBgCgoKYGBge3IgbGlicmFyeX0KbGlicmFyeShkcGx5cikKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoZ2dwbG90MikKCi5saWJQYXRocygpCmBgYAoKIyBQcmVwYXJhdGlvbgoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBzZXQgdGhlIGdsb2JhbCBzZXR0aW5ncyBvZiB0aGUgYW5hbHlzaXMuIFdlIHdpbGwgc3RvcmUgZGF0YSB0aGVyZSA6CgpgYGB7ciBvdXRfZGlyfQpvdXRfZGlyID0gIi4iCmBgYAoKV2UgbG9hZCB0aGUgcGFyYW1ldGVycyA6CgpgYGB7ciBnZXRfcGFyYW19CnNhbXBsZV9uYW1lID0gcGFyYW1zJHNhbXBsZV9uYW1lICMgIjIwMjFfMzEiCiMgc2FtcGxlX25hbWUgPSAiMjAyMV8zMSIKYGBgCgpJbnB1dCBjb3VudCBtYXRyaXggaXMgdGhlcmUgOgoKYGBge3IgY291bnRfbWF0cml4X2Rpcn0KY291bnRfbWF0cml4X2RpciA9IHBhc3RlMChvdXRfZGlyLCAiL2lucHV0LyIsIHNhbXBsZV9uYW1lKQpgYGAKCgpXZSBsb2FkIHRoZSBtYXJrZXJzIGFuZCBzcGVjaWZpYyBjb2xvcnMgZm9yIGVhY2ggY2VsbCB0eXBlIDoKCmBgYHtyIGNlbGxfbWFya2Vyc30KY2VsbF9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAob3V0X2RpciwgIi8uLi8xX21ldGFkYXRhL2hzX2hkX2NlbGxfbWFya2Vycy5yZHMiKSkKY2VsbF9tYXJrZXJzID0gbGFwcGx5KGNlbGxfbWFya2VycywgRlVOID0gdG91cHBlcikKbGVuZ3RocyhjZWxsX21hcmtlcnMpCmBgYAoKSGVyZSBhcmUgY3VzdG9tIGNvbG9ycyBmb3IgZWFjaCBjZWxsIHR5cGUgOgoKYGBge3IgY29sb3JfbWFya2VycywgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KY29sb3JfbWFya2VycyA9IHJlYWRSRFMocGFzdGUwKG91dF9kaXIsICIvLi4vMV9tZXRhZGF0YS9oc19oZF9jb2xvcl9tYXJrZXJzLnJkcyIpKQoKZGF0YS5mcmFtZShjZWxsX3R5cGUgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSwKICAgICAgICAgICBjb2xvciA9IHVubGlzdChjb2xvcl9tYXJrZXJzKSkgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KC4sIGFlcyh4ID0gY2VsbF90eXBlLCB5ID0gMCwgZmlsbCA9IGNlbGxfdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHBjaCA9IDIxLCBzaXplID0gNSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHVubGlzdChjb2xvcl9tYXJrZXJzKSwgYnJlYWtzID0gbmFtZXMoY29sb3JfbWFya2VycykpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCgpXZSBsb2FkIG1hcmtlcnMgdG8gZGlzcGxheSBvbiB0aGUgZG90cGxvdCA6CgpgYGB7ciBkb3RwbG90X21hcmtlcnN9CmRvdHBsb3RfbWFya2VycyA9IHJlYWRSRFMocGFzdGUwKG91dF9kaXIsICIvLi4vMV9tZXRhZGF0YS9oc19oZF9kb3RwbG90X21hcmtlcnMucmRzIikpCmRvdHBsb3RfbWFya2VycyA9IGxhcHBseShkb3RwbG90X21hcmtlcnMsIEZVTiA9IHRvdXBwZXIpCmRvdHBsb3RfbWFya2VycwpgYGAKCgpXZSBsb2FkIG1ldGFkYXRhIGZvciB0aGlzIHNhbXBsZSA6CgpgYGB7ciBzYW1wbGVfaW5mb30Kc2FtcGxlX2luZm8gPSByZWFkUkRTKHBhc3RlMChvdXRfZGlyLCAiLy4uLzFfbWV0YWRhdGEvaHNfaGRfc2FtcGxlX2luZm8ucmRzIikpCnNhbXBsZV9pbmZvICU+JQogIGRwbHlyOjpmaWx0ZXIocHJvamVjdF9uYW1lID09IHNhbXBsZV9uYW1lKQpgYGAKCgpUaGVzZSBpcyBhIHBhcmFtZXRlciBmb3IgZGlmZmVyZW50IGZ1bmN0aW9ucyA6CgpgYGB7ciBnbG9iYWxfc2V0dGluZ3N9CmNsID0gYXF1YXJpdXM6OmNyZWF0ZV9wYXJhbGxlbF9pbnN0YW5jZShudGhyZWFkcyA9IDNMKQpjdXRfbG9nX25Db3VudF9STkEgPSA2CmN1dF9uRmVhdHVyZV9STkEgPSA1MDAKY3V0X3BlcmNlbnQubXQgPSAyMApjdXRfcGVyY2VudC5yYiA9IDUwCmBgYAoKIyBMb2FkIGNvdW50IG1hdHJpeAoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBsb2FkIHRoZSByYXcgY291bnQgbWF0cml4LiBUaGVuLCB3ZSBhcHBsaWVkIGFuIGVtcHR5IGRyb3BsZXRzIGZpbHRlcmluZy4KCmBgYHtyIGxvYWRfY291bnRfbWF0cml4LCB0aW1lX2l0ID0gVFJVRX0Kc29iaiA9IGFxdWFyaXVzOjpsb2FkX3NjX2RhdGEoZGF0YV9wYXRoID0gY291bnRfbWF0cml4X2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX25hbWUgPSBzYW1wbGVfbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXlfc2VlZCA9IDEzMzdMKQpzb2JqCmBgYAoKSW4gZ2VuZXMgbWV0YWRhdGEsIHdlIGFkZCB0aGUgRW5zZW1ibCBJRC4gVGhlIGBzb2JqQGFzc2F5cyRSTkFAbWV0YS5mZWF0dXJlc2AgZGF0YWZyYW1lIGNvbnRhaW5zIHRocmVlIGluZm9ybWF0aW9uIDoKCiogYHJvd25hbWVzYCA6IGdlbmUgbmFtZXMgc3RvcmVkIGFzIHRoZSBkaW1uYW1lcyBvZiB0aGUgY291bnQgbWF0cml4LiBEdXBsaWNhdGVkIGdlbmUgbmFtZXMgd2lsbCBoYXZlIGEgYC4xYCBhdCB0aGUgZW5kIG9mIHRoZWlyIG5hbWUKKiBgRW5zZW1ibF9JRGAgOiBFbnNlbWJsSUQsIGFzIHN0b3JlZCBpbiB0aGUgYGZlYXR1cmVzLnRzdi5nemAgZmlsZQoqIGBnZW5lX25hbWVgIDogZ2VuZV9uYW1lLCBhcyBzdG9yZWQgaW4gdGhlIGBmZWF0dXJlcy50c3YuZ3pgIGZpbGUuIER1cGxpY2F0ZWQgZ2VuZSBuYW1lcyB3aWxsIGhhdmUgdGhlIHNhbWUgbmFtZS4KCmBgYHtyIGZlYXR1cmVzX2RmfQpmZWF0dXJlc19kZiA9IHJlYWQuY3N2KHBhc3RlMChjb3VudF9tYXRyaXhfZGlyLCAiL2ZlYXR1cmVzLnRzdi5neiIpLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSAwKQpmZWF0dXJlc19kZiA9IGZlYXR1cmVzX2RmWywgYygxOjIpXQpjb2xuYW1lcyhmZWF0dXJlc19kZikgPSBjKCJFbnNlbWJsX0lEIiwgImdlbmVfbmFtZSIpCnJvd25hbWVzKGZlYXR1cmVzX2RmKSA9IHJvd25hbWVzKHNvYmopICMgbWFuZGF0b3J5IGZvciBTZXVyYXQ6OkZpbmRWYXJpYWJsZUZlYXR1cmVzCgpzb2JqQGFzc2F5cyRSTkFAbWV0YS5mZWF0dXJlcyA9IGZlYXR1cmVzX2RmCnJtKGZlYXR1cmVzX2RmKQoKaGVhZChzb2JqQGFzc2F5cyRSTkFAbWV0YS5mZWF0dXJlcykKYGBgCgpXZSBhZGQgdGhlIHNhbWUgY29sdW1ucyBhcyBpbiBtZXRhZGF0YSA6CgpgYGB7ciBhZGRfbWV0YWRhdGF9CnJvd19vaSA9IChzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUgPT0gc2FtcGxlX25hbWUpCgpzb2JqJHByb2plY3RfbmFtZSA9IHNhbXBsZV9uYW1lCnNvYmokc2FtcGxlX2lkZW50aWZpZXIgPSBzYW1wbGVfaW5mb1tyb3dfb2ksICJzYW1wbGVfaWRlbnRpZmllciJdCnNvYmokc2FtcGxlX3R5cGUgPSBzYW1wbGVfaW5mb1tyb3dfb2ksICJzYW1wbGVfdHlwZSJdCgpjb2xuYW1lcyhzb2JqQG1ldGEuZGF0YSkKYGBgCgojIEJlZm9yZSBmaWx0ZXJpbmcKCiMjIE5vcm1hbGl6YXRpb24KCmBgYHtyIG5vcm1hbGl6YXRpb259CnNvYmogPSBTZXVyYXQ6Ok5vcm1hbGl6ZURhdGEoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIpCgpzb2JqID0gU2V1cmF0OjpGaW5kVmFyaWFibGVGZWF0dXJlcyhzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZmVhdHVyZXMgPSAzMDAwKQpzb2JqCmBgYAoKIyMgUHJvamVjdGlvbgoKV2UgZ2VuZXJhdGUgYSB0U05FIHRvIHZpc3VhbGl6ZSBjZWxscyBiZWZvcmUgZmlsdGVyaW5nLgoKYGBge3IgcGNhX2JlZm9yZSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA0fQpzb2JqID0gYXF1YXJpdXM6OmRpbWVuc2lvbnNfcmVkdWN0aW9uKHNvYmogPSBzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInBjYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4X2RpbXMgPSAxMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQpTZXVyYXQ6OkVsYm93UGxvdChzb2JqLCBuZGltcyA9IDEwMCwgcmVkdWN0aW9uID0gIlJOQV9wY2EiKQpgYGAKCgpXZSBnZW5lcmF0ZSBhIHRTTkUgd2l0aCAyMCBwcmluY2lwYWwgY29tcG9uZW50cyA6CgpgYGB7ciB0c25lX2JlZm9yZX0KbmRpbXMgPSAyMApzb2JqID0gU2V1cmF0OjpSdW5UU05FKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gIlJOQV9wY2EiLAogICAgICAgICAgICAgICAgICAgICAgIGRpbXMgPSAxOm5kaW1zLAogICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gMTMzN0wsCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uLm5hbWUgPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdHNuZSIpKQoKc29iagpgYGAKCiMjIENlbGwgdHlwZQoKV2UgYW5ub3RhdGUgY2VsbHMgZm9yIGNlbGwgdHlwZSB1c2luZyBgU2V1cmF0OjpBZGRNb2R1bGVTY29yZWAgZnVuY3Rpb24uCgpgYGB7ciBjZWxsX2Fubm90X2N1c3RvbV9zaG9ydCwgdGltZV9pdCA9IFRSVUV9CnNvYmogPSBhcXVhcml1czo6Y2VsbF9hbm5vdF9jdXN0b20oc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXduYW1lID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFya2VycyA9IGNlbGxfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VfbmVnYXRpdmUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFkZF9zY29yZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUpCgpjb2xuYW1lcyhzb2JqQG1ldGEuZGF0YSkgPSBzdHJpbmdyOjpzdHJfcmVwbGFjZV9hbGwoc3RyaW5nID0gY29sbmFtZXMoc29iakBtZXRhLmRhdGEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICIgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIl8iKQoKc29iaiRjZWxsX3R5cGUgPSBmYWN0b3Ioc29iaiRjZWxsX3R5cGUsIGxldmVscyA9IG5hbWVzKGNlbGxfbWFya2VycykpCgp0YWJsZShzb2JqJGNlbGxfdHlwZSkKYGBgCgpUbyBqdXN0aWZ5IGNlbGwgdHlwZSBhbm5vdGF0aW9uLCB3ZSBjYW4gbWFrZSBhIGRvdHBsb3QgOgoKYGBge3IgZG90cGxvdF9jZWxsX3R5cGVfc2hvcnQsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOX0KbWFya2VycyA9IGMoIlBUUFJDIiwgIk1TWDIiLCAiS1JUMTYiLAogICAgICAgICAgICB1bmlxdWUodW5saXN0KGRvdHBsb3RfbWFya2Vyc1tsZXZlbHMoc29iaiRjZWxsX3R5cGUpXSkpKQptYXJrZXJzID0gbWFya2Vyc1ttYXJrZXJzICVpbiUgcm93bmFtZXMoc29iaildCgphcXVhcml1czo6cGxvdF9kb3RwbG90KHNvYmosIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWUgPSAiY2VsbF90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICBtYXJrZXJzID0gbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICBuYl9obGluZSA9IDApICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6Ojpjb2xvcl9nZW5lKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwKICAgICAgICAgICAgICAgICBsZWdlbmQuYm94ID0gInZlcnRpY2FsIiwKICAgICAgICAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQpgYGAKCldlIGNhbiBtYWtlIGEgYmFycGxvdCB0byBzZWUgdGhlIGNvbXBvc2l0aW9uIG9mIGVhY2ggZGF0YXNldCwgYW5kIHZpc3VhbGl6ZSBjZWxsIHR5cGVzIG9uIHRoZSBwcm9qZWN0aW9uLgoKYGBge3IgYmFycGxvdF9jZWxsdHlwZSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDgsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpkZl9wcm9wb3J0aW9uID0gYXMuZGF0YS5mcmFtZShwcm9wLnRhYmxlKHRhYmxlKHNvYmokb3JpZy5pZGVudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqJGNlbGxfdHlwZSkpKQpjb2xuYW1lcyhkZl9wcm9wb3J0aW9uKSA9IGMoIm9yaWcuaWRlbnQiLCAiY2VsbF90eXBlIiwgImZyZXEiKQoKcXVhbnRpZiA9IHRhYmxlKHNvYmokb3JpZy5pZGVudCkgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJvcmlnLmlkZW50IiwgIm5iX2NlbGxzIikpCgojIFBsb3QKcGxvdF9saXN0ID0gbGlzdCgpCgpwbG90X2xpc3RbWzJdXSA9IGFxdWFyaXVzOjpwbG90X2JhcnBsb3QoZGYgPSBkZl9wcm9wb3J0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJvcmlnLmlkZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAiZnJlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IGdncGxvdDI6OnBvc2l0aW9uX2ZpbGwoKSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiQ2VsbCB0eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjb2xvcl9tYXJrZXJzW2xldmVscyhkZl9wcm9wb3J0aW9uJGNlbGxfdHlwZSldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGxldmVscyhkZl9wcm9wb3J0aW9uJGNlbGxfdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX2xhYmVsKGRhdGEgPSBxdWFudGlmLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBvcmlnLmlkZW50LCB5ID0gMS4wNSwgbGFiZWwgPSBuYl9jZWxscyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gMCkKCnBsb3RfbGlzdFtbMV1dID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdW5saXN0KGNvbG9yX21hcmtlcnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSBzYW1wbGVfbmFtZSwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKG5jb2woc29iaiksICIgY2VsbHMiKSkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKSArIFNldXJhdDo6Tm9BeGVzKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBucm93ID0gMSwgd2lkdGhzID0gYyg2LCAxKSkKYGBgCgojIyBDZWxsIGN5Y2xlIHBoYXNlCgpXZSBhbm5vdGF0ZSBjZWxscyBmb3IgY2VsbCBjeWNsZSBwaGFzZSB1c2luZyBgU2V1cmF0YCBhbmQgYGN5Y2xvbmVgLgoKYGBge3IgY2VsbF9jeWNsZSwgdGltZV9pdCA9IFRSVUV9CmNjX2NvbHVtbnMgPSBhcXVhcml1czo6YWRkX2NlbGxfY3ljbGUoc29iaiA9IHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGVjaWVzX3JkeCA9ICJocyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQlBQQVJBTSA9IGNsKUBtZXRhLmRhdGFbLCBjKCJTZXVyYXQuUGhhc2UiLCAiUGhhc2UiKV0KCnNvYmokU2V1cmF0LlBoYXNlID0gY2NfY29sdW1ucyRTZXVyYXQuUGhhc2UKc29iaiRjeWNsb25lLlBoYXNlID0gY2NfY29sdW1ucyRQaGFzZQoKdGFibGUoc29iaiRTZXVyYXQuUGhhc2UsIHNvYmokY3ljbG9uZS5QaGFzZSkKYGBgCgpXZSB2aXN1YWxpemUgY2VsbCBjeWNsZSBvbiB0aGUgcHJvamVjdGlvbiA6CgpgYGB7ciBzZWVfY2VsbF9jeWNsZTEsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNywgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnBsb3RfbGlzdCA9IGxpc3QoKQoKcGxvdF9saXN0W1syXV0gPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiU2V1cmF0LlBoYXNlIikgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiQ2VsbCBDeWNsZSBQaGFzZSIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJTZXVyYXQuUGhhc2UiKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpICsgU2V1cmF0OjpOb0F4ZXMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnBsb3RfbGlzdFtbMV1dID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImN5Y2xvbmUuUGhhc2UiKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJDZWxsIEN5Y2xlIFBoYXNlIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gImN5Y2xvbmUuUGhhc2UiKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpICsgU2V1cmF0OjpOb0F4ZXMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5yb3cgPSAxKQpgYGAKCiMgUXVhbGl0eSBjb250cm9sCgpJbiB0aGlzIHNlY3Rpb24sIHdlIGxvb2sgYXQgdGhlIG51bWJlciBvZiBnZW5lcyBleHByZXNzZWQgYnkgZWFjaCBjZWxsLCB0aGUgbnVtYmVyIG9mIFVNSSwgdGhlIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCBnZW5lcyBleHByZXNzZWQsIGFuZCB0aGUgcGVyY2VudGFnZSBvZiByaWJvc29tYWwgZ2VuZXMgZXhwcmVzc2VkLiBUaGVuLCB3aXRob3V0IHRha2luZyBpbnRvIGFjY291bnQgdGhlIGNlbGxzIGV4cHJlc3NpbmcgbG93IG51bWJlciBvZiBnZW5lcyBvciBoYXZlIGxvdyBudW1iZXIgb2YgVU1JLCB3ZSBpZGVudGlmeSBkb3VibGV0IGNlbGxzLgoKV2UgY29tcHV0ZSBmb3VyIHF1YWxpdHkgbWV0cmljcyA6CgpgYGB7ciBxY19tZXRyaWNzfQpzb2JqID0gU2V1cmF0OjpQZXJjZW50YWdlRmVhdHVyZVNldChzb2JqLCBwYXR0ZXJuID0gIl5NVCIsIGNvbC5uYW1lID0gInBlcmNlbnQubXQiKQpzb2JqID0gU2V1cmF0OjpQZXJjZW50YWdlRmVhdHVyZVNldChzb2JqLCBwYXR0ZXJuID0gIl5SUFtMfFNdWzAtOV0qJCIsIGNvbC5uYW1lID0gInBlcmNlbnQucmIiKQoKaGVhZChzb2JqQG1ldGEuZGF0YSkKYGBgCgpXZSBnZXQgdGhlIGNlbGwgYmFyY29kZXMgZm9yIHRoZSBmYWlsaW5nIGNlbGxzIDoKCmBgYHtyIGZhaWxlZH0KZmFpbF9wZXJjZW50Lm10ID0gc29iakBtZXRhLmRhdGEgJT4lIGRwbHlyOjpmaWx0ZXIocGVyY2VudC5tdCA+IGN1dF9wZXJjZW50Lm10KSAlPiUgcm93bmFtZXMoKQpmYWlsX3BlcmNlbnQucmIgPSBzb2JqQG1ldGEuZGF0YSAlPiUgZHBseXI6OmZpbHRlcihwZXJjZW50LnJiID4gY3V0X3BlcmNlbnQucmIpICU+JSByb3duYW1lcygpCmZhaWxfbG9nX25Db3VudF9STkEgPSBzb2JqQG1ldGEuZGF0YSAlPiUgZHBseXI6OmZpbHRlcihsb2dfbkNvdW50X1JOQSA8IGN1dF9sb2dfbkNvdW50X1JOQSkgJT4lIHJvd25hbWVzKCkKZmFpbF9uRmVhdHVyZV9STkEgPSBzb2JqQG1ldGEuZGF0YSAlPiUgZHBseXI6OmZpbHRlcihuRmVhdHVyZV9STkEgPCBjdXRfbkZlYXR1cmVfUk5BKSAlPiUgcm93bmFtZXMoKQpgYGAKCgojIyBEb3VibGV0IGNlbGxzIGRldGVjdGlvbgoKV2l0aG91dCB0YWtpbmcgaW50byBhY2NvdW50IHRoZSBsb3cgVU1JIGFuZCBsb3cgbnVtYmVyIG9mIGZlYXR1cmVzIGNlbGxzLCB3ZSBpZGVudGlmeSBkb3VibGV0cy4KCmBgYHtyIGZzb2JqfQpmc29iaiA9IHN1YnNldChzb2JqLCBpbnZlcnQgPSBUUlVFLAogICAgICAgICAgICAgICBjZWxscyA9IHVuaXF1ZShjKGZhaWxfbG9nX25Db3VudF9STkEsIGZhaWxfbkZlYXR1cmVfUk5BKSkpCmZzb2JqCmBgYAoKT24gdGhpcyBmaWx0ZXJlZCBkYXRhc2V0LCB3ZSBhcHBseSBkb3VibGV0IGNlbGxzIGRldGVjdGlvbi4gSnVzdCBiZWZvcmUsIHdlIHJ1biB0aGUgbm9ybWFsaXphdGlvbiwgdGFraW5nIGludG8gYWNjb3VudCBvbmx5IHRoZSByZW1haW5pbmcgY2VsbHMuCgpgYGB7ciBub3JtYWxpemF0aW9uX2RvdWJsZXRzfQpzb2JqID0gU2V1cmF0OjpOb3JtYWxpemVEYXRhKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiKQoKc29iaiA9IFNldXJhdDo6RmluZFZhcmlhYmxlRmVhdHVyZXMoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZlYXR1cmVzID0gMzAwMCkKc29iagpgYGAKCldlIGlkZW50aWZ5IGRvdWJsZXQgY2VsbHMgOgoKYGBge3IgZG91YmxldF9kZXRlY3Rpb24sIHRpbWVfaXQgPSBUUlVFfQpmc29iaiA9IGFxdWFyaXVzOjpmaW5kX2RvdWJsZXRzKHNvYmogPSBmc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCUFBBUkFNID0gY2wpCgpmYWlsX2RvdWJsZXRzX2NvbnNlbnN1cyA9IFNldXJhdDo6V2hpY2hDZWxscyhmc29iaiwgZXhwcmVzc2lvbiA9IGRvdWJsZXRzX2NvbnNlbnN1cy5jbGFzcykKZmFpbF9kb3VibGV0c19zY0RibEZpbmRlciA9IFNldXJhdDo6V2hpY2hDZWxscyhmc29iaiwgZXhwcmVzc2lvbiA9IHNjRGJsRmluZGVyLmNsYXNzKQpmYWlsX2RvdWJsZXRzX2h5YnJpZCA9IFNldXJhdDo6V2hpY2hDZWxscyhmc29iaiwgZXhwcmVzc2lvbiA9IGh5YnJpZF9zY29yZS5jbGFzcykKYGBgCgpXZSBhZGQgdGhlIGluZm9ybWF0aW9uIGluIHRoZSBub24gZmlsdGVyZWQgU2V1cmF0IG9iamVjdCA6CgpgYGB7ciBhZGRfZG91YmxldF9tZXRyaWNzfQpzb2JqJGRvdWJsZXRzX2NvbnNlbnN1cy5jbGFzcyA9IGRwbHlyOjpjYXNlX3doZW4oIShjb2xuYW1lcyhzb2JqKSAlaW4lIGNvbG5hbWVzKGZzb2JqKSkgfiBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzKHNvYmopICVpbiUgZmFpbF9kb3VibGV0c19jb25zZW5zdXMgfiBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIShjb2xuYW1lcyhzb2JqKSAlaW4lIGZhaWxfZG91YmxldHNfY29uc2Vuc3VzKSB+IEZBTFNFKQoKc29iaiRzY0RibEZpbmRlci5jbGFzcyA9IGRwbHlyOjpjYXNlX3doZW4oIShjb2xuYW1lcyhzb2JqKSAlaW4lIGNvbG5hbWVzKGZzb2JqKSkgfiBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbmFtZXMoc29iaikgJWluJSBmYWlsX2RvdWJsZXRzX3NjRGJsRmluZGVyIH4gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIShjb2xuYW1lcyhzb2JqKSAlaW4lIGZhaWxfZG91YmxldHNfc2NEYmxGaW5kZXIpIH4gRkFMU0UpCgpzb2JqJGh5YnJpZF9zY29yZS5jbGFzcyA9IGRwbHlyOjpjYXNlX3doZW4oIShjb2xuYW1lcyhzb2JqKSAlaW4lIGNvbG5hbWVzKGZzb2JqKSkgfiBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzKHNvYmopICVpbiUgZmFpbF9kb3VibGV0c19oeWJyaWQgfiBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIShjb2xuYW1lcyhzb2JqKSAlaW4lIGZhaWxfZG91YmxldHNfaHlicmlkKSB+IEZBTFNFKQpgYGAKCmBgYHtyIGNsZWFuX2Zzb2JqLCBlY2hvID0gRkFMU0V9CnJtKGZzb2JqKQpgYGAKCgojIyBRdWFsaXR5IGNvbnRyb2wgcmVwcmVzZW50YXRpb24KCldlIGNhbiB2aXN1YWxpemUgdGhlIDQgY2VsbHMgcXVhbGl0eSB3aXRoIGEgVmVubiBkaWFncmFtIDogCgpgYGB7ciBxY192ZW5uLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDZ9Cm5fZmlsdGVyZWQgPSBjKGZhaWxfcGVyY2VudC5tdCwgZmFpbF9wZXJjZW50LnJiLCBmYWlsX2xvZ19uQ291bnRfUk5BLCBmYWlsX25GZWF0dXJlX1JOQSkgJT4lCiAgdW5pcXVlKCkgJT4lIGxlbmd0aCgpCnBlcmNlbnRfZmlsdGVyZWQgPSByb3VuZCgxMDAqKG5fZmlsdGVyZWQvbmNvbChzb2JqKSksIDIpCgpnZ3Zlbm46OmdndmVubihsaXN0KHBlcmNlbnQubXQgPSBmYWlsX3BlcmNlbnQubXQsCiAgICAgICAgICAgICAgICAgICAgcGVyY2VudC5yYiA9IGZhaWxfcGVyY2VudC5yYiwKICAgICAgICAgICAgICAgICAgICBsb2dfbkNvdW50X1JOQSA9IGZhaWxfbG9nX25Db3VudF9STkEsCiAgICAgICAgICAgICAgICAgICAgbkZlYXR1cmVfUk5BID0gZmFpbF9uRmVhdHVyZV9STkEpLCAKICAgICAgICAgICAgICAgZmlsbF9jb2xvciA9IGMoIiMwMDczQzJGRiIsICIjRUZDMDAwRkYiLCAib3JhbmdlIiwgInBpbmsiKSwKICAgICAgICAgICAgICAgc3Ryb2tlX3NpemUgPSAwLjUsIHNldF9uYW1lX3NpemUgPSA0KSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJGaWx0ZXJlZCBvdXQgY2VsbHMiLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAobl9maWx0ZXJlZCwgIiBjZWxscyAoIiwgcGVyY2VudF9maWx0ZXJlZCwgIiAlIG9mIGFsbCBjZWxscykiKSkgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCgojIyMgTnVtYmVyIG9mIFVNSQoKVG8gdmlzdWFsaXplIHRoZSB0aHJlc2hvbGQgZm9yIG51bWJlciBvZiBVTUksIHdlIGNhbiBtYWtlIGEgaGlzdG9ncmFtIDoKCmBgYHtyIHFjX3VtaV9oaXN0LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA1fQphcXVhcml1czo6cGxvdF9xY19kZW5zaXR5KGRmID0gc29iakBtZXRhLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJsb2dfbkNvdW50X1JOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucyA9IDIwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieSA9ICJvcmlnLmlkZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9jb2xvciA9IHNldE5hbWVzKHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm0gPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmlhbnQpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHhfdGhyZXNoID0gY3V0X2xvZ19uQ291bnRfUk5BKQpgYGAKCmBgYHtyIHZsbl91bWlfY2VsbF90eXBlLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDZ9ClNldXJhdDo6VmxuUGxvdChzb2JqLCBmZWF0dXJlcyA9ICJsb2dfbkNvdW50X1JOQSIsIHB0LnNpemUgPSAwLjAwMSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JfbWFya2VycywgYnJlYWtzID0gbmFtZXMoY29sb3JfbWFya2VycykpICsKICBnZ3Bsb3QyOjpnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjdXRfbG9nX25Db3VudF9STkEsIGNvbCA9ICJyZWQiKSArCiAgZ2dwbG90Mjo6bGFicyh4ID0gIiIpCmBgYAoKYGBge3IgcWNfdW1pX3Byb2osIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA3fQpzb2JqJGZhaWwgPSBpZmVsc2UoY29sbmFtZXMoc29iaikgJWluJSBmYWlsX2xvZ19uQ291bnRfUk5BLAogICAgICAgICAgICAgICAgICAgeWVzID0gYXMuY2hhcmFjdGVyKHNvYmokY2VsbF90eXBlKSwgbm8gPSBOQSkKc29iaiRmYWlsID0gZmFjdG9yKHNvYmokZmFpbCwgbGV2ZWxzID0gYyhsZXZlbHMoc29iaiRjZWxsX3R5cGUpLCBOQSkpCgpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiZmFpbCIsIG5hLnZhbHVlID0gImdyYXk4MCIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJsb2dfbkNvdW50X1JOQSIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChsZW5ndGgoZmFpbF9sb2dfbkNvdW50X1JOQSksICIgY2VsbHMiKSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmBgYAoKIyMjIE51bWJlciBvZiBmZWF0dXJlcwoKVG8gdmlzdWFsaXplIHRoZSB0aHJlc2hvbGQgZm9yIG51bWJlciBvZiBmZWF0dXJlcywgd2UgY2FuIG1ha2UgYSBoaXN0b2dyYW0gOgoKYGBge3IgcWNfZmVhdHVyZXNfaGlzdCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNX0KYXF1YXJpdXM6OnBsb3RfcWNfZGVuc2l0eShkZiA9IHNvYmpAbWV0YS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAibkZlYXR1cmVfUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBiaW5zID0gMjAwLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5ID0gIm9yaWcuaWRlbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2NvbG9yID0gc2V0TmFtZXMoc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubSA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWFudCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgeF90aHJlc2ggPSBjdXRfbkZlYXR1cmVfUk5BKQpgYGAKCgpgYGB7ciB2bG5fZmVhdHVyZXNfY2VsbF90eXBlLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDZ9ClNldXJhdDo6VmxuUGxvdChzb2JqLCBmZWF0dXJlcyA9ICJuRmVhdHVyZV9STkEiLCBwdC5zaXplID0gMC4wMDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9yX21hcmtlcnMsIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpKSArCiAgZ2dwbG90Mjo6Z2VvbV9obGluZSh5aW50ZXJjZXB0ID0gY3V0X25GZWF0dXJlX1JOQSwgY29sID0gInJlZCIpICsKICBnZ3Bsb3QyOjpsYWJzKHggPSAiIikKYGBgCgpgYGB7ciBxY19mZWF0dXJlc19wcm9qLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gN30Kc29iaiRmYWlsID0gaWZlbHNlKGNvbG5hbWVzKHNvYmopICVpbiUgZmFpbF9uRmVhdHVyZV9STkEsCiAgICAgICAgICAgICAgICAgICB5ZXMgPSBhcy5jaGFyYWN0ZXIoc29iaiRjZWxsX3R5cGUpLCBubyA9IE5BKQpzb2JqJGZhaWwgPSBmYWN0b3Ioc29iaiRmYWlsLCBsZXZlbHMgPSBjKGxldmVscyhzb2JqJGNlbGxfdHlwZSksIE5BKSkKClNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJmYWlsIiwgbmEudmFsdWUgPSAiZ3JheTgwIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIm5GZWF0dXJlX1JOQSIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChsZW5ndGgoZmFpbF9uRmVhdHVyZV9STkEpLCAiIGNlbGxzIikpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCiMjIyBNaXRvY2hvbmRyaWFsIGdlbmVzIGV4cHJlc3Npb24KClRvIGlkZW50aWZ5IGEgdGhyZXNob2xkIGZvciBtaXRvY2hvbmRyaWFsIGdlbmUgZXhwcmVzc2lvbiwgd2UgY2FuIG1ha2UgYSBoaXN0b2dyYW0gOgoKYGBge3IgcWNfbWl0b19oaXN0LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA1fQphcXVhcml1czo6cGxvdF9xY19kZW5zaXR5KGRmID0gc29iakBtZXRhLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJwZXJjZW50Lm10IiwKICAgICAgICAgICAgICAgICAgICAgICAgICBiaW5zID0gMjAwLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5ID0gIm9yaWcuaWRlbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2NvbG9yID0gc2V0TmFtZXMoc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubSA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWFudCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgeF90aHJlc2ggPSBjdXRfcGVyY2VudC5tdCkKYGBgCgpgYGB7ciB2bG5fcGVyY2VudG10X2NlbGxfdHlwZSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQpTZXVyYXQ6OlZsblBsb3Qoc29iaiwgZmVhdHVyZXMgPSAicGVyY2VudC5tdCIsIHB0LnNpemUgPSAwLjAwMSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JfbWFya2VycywgYnJlYWtzID0gbmFtZXMoY29sb3JfbWFya2VycykpICsKICBnZ3Bsb3QyOjpnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjdXRfcGVyY2VudC5tdCwgY29sID0gInJlZCIpICsKICBnZ3Bsb3QyOjpsYWJzKHggPSAiIikKYGBgCgpgYGB7ciBxY19taXRvX3Byb2osIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA3fQpzb2JqJGZhaWwgPSBpZmVsc2UoY29sbmFtZXMoc29iaikgJWluJSBmYWlsX3BlcmNlbnQubXQsCiAgICAgICAgICAgICAgICAgICB5ZXMgPSBhcy5jaGFyYWN0ZXIoc29iaiRjZWxsX3R5cGUpLCBubyA9IE5BKQpzb2JqJGZhaWwgPSBmYWN0b3Ioc29iaiRmYWlsLCBsZXZlbHMgPSBjKGxldmVscyhzb2JqJGNlbGxfdHlwZSksIE5BKSkKClNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJmYWlsIiwgbmEudmFsdWUgPSAiZ3JheTgwIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gInBlcmNlbnQubXQiLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAobGVuZ3RoKGZhaWxfcGVyY2VudC5tdCksICIgY2VsbHMiKSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmBgYAoKIyMjIFJpYm9zb21hbCBnZW5lcyBleHByZXNzaW9uCgpUbyBpZGVudGlmeSBhIHRocmVzaG9sZCBmb3Igcmlib3NvbWFsIGdlbmUgZXhwcmVzc2lvbiwgd2UgY2FuIG1ha2UgYSBoaXN0b2dyYW0gOgoKYGBge3IgcWNfcmlib19oaXN0LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA1fQphcXVhcml1czo6cGxvdF9xY19kZW5zaXR5KGRmID0gc29iakBtZXRhLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJwZXJjZW50LnJiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBiaW5zID0gMjAwLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5ID0gIm9yaWcuaWRlbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2NvbG9yID0gc2V0TmFtZXMoc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubSA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWFudCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgeF90aHJlc2ggPSBjdXRfcGVyY2VudC5yYikKYGBgCgoKYGBge3IgdmxuX3BlcmNlbnRyYl9jZWxsX3R5cGUsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNn0KU2V1cmF0OjpWbG5QbG90KHNvYmosIGZlYXR1cmVzID0gInBlcmNlbnQucmIiLCBwdC5zaXplID0gMC4wMDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9yX21hcmtlcnMsIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpKSArCiAgZ2dwbG90Mjo6Z2VvbV9obGluZSh5aW50ZXJjZXB0ID0gY3V0X3BlcmNlbnQucmIsIGNvbCA9ICJyZWQiKSArCiAgZ2dwbG90Mjo6bGFicyh4ID0gIiIpCmBgYAoKYGBge3IgcWNfcmlib19wcm9qLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gN30Kc29iaiRmYWlsID0gaWZlbHNlKGNvbG5hbWVzKHNvYmopICVpbiUgZmFpbF9wZXJjZW50LnJiLAogICAgICAgICAgICAgICAgICAgeWVzID0gYXMuY2hhcmFjdGVyKHNvYmokY2VsbF90eXBlKSwgbm8gPSBOQSkKc29iaiRmYWlsID0gZmFjdG9yKHNvYmokZmFpbCwgbGV2ZWxzID0gYyhsZXZlbHMoc29iaiRjZWxsX3R5cGUpLCBOQSkpCgpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiZmFpbCIsIG5hLnZhbHVlID0gImdyYXk4MCIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJwZXJjZW50LnJiIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKGxlbmd0aChmYWlsX3BlcmNlbnQucmIpLCAiIGNlbGxzIikpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCiMjIyBGQUNTLWxpa2UgZmlndXJlCgpXZSB3b3VsZCBsaWtlIHRvIHNlZSBpZiB0aGUgbnVtYmVyIG9mIGZlYXR1cmUgZXhwcmVzc2VkIGJ5IGNlbGwsIGFuZCB0aGUgbnVtYmVyIG9mIFVNSSBpcyBjb3JyZWxhdGVkIHdpdGggdGhlIGNlbGwgdHlwZSwgdGhlIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCBhbmQgcmlib3NvbWFsIGdlbmUgZXhwcmVzc2VkLCBhbmQgdGhlIGRvdWJsZXQgc3RhdHVzLiBXZSBidWlsZCB0aGUgYGxvZ19uQ291bnRfUk5BYCBieSBgbkZlYXR1cmVfUk5BYCBmaWd1cmUsIHdoZXJlIGNlbGxzIChkb3RzKSBhcmUgY29sb3JlZCBieSB0aGVzZSBkaWZmZXJlbnQgbWV0cmljcy4KClRoaXMgaXMgdGhlIGZpZ3VyZSwgY29sb3JlZCBieSBjZWxsIHR5cGUgOgoKYGBge3IgcWNfcGF0Y2h3b3JrX2NlbGxfdHlwZSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gOH0KYXF1YXJpdXM6OnBsb3RfcWNfZmFjc2xpa2UoZGYgPSBzb2JqQG1ldGEuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJuRmVhdHVyZV9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gImxvZ19uQ291bnRfUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sX2J5ID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbF9jb2xvcnMgPSB1bm5hbWUoY29sb3JfbWFya2VycyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHhfdGhyZXNoID0gY3V0X25GZWF0dXJlX1JOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeV90aHJlc2ggPSBjdXRfbG9nX25Db3VudF9STkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJpbnMgPSAyMDApCmBgYAoKVGhpcyBpcyB0aGUgZmlndXJlLCBjb2xvcmVkIGJ5IHRoZSBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgZ2VuZXMgZXhwcmVzc2VkIGluIGNlbGwgOgoKYGBge3IgcWNfcGF0Y2h3b3JrX21pdG8sIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDh9CmFxdWFyaXVzOjpwbG90X3FjX2ZhY3NsaWtlKGRmID0gc29iakBtZXRhLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAibkZlYXR1cmVfUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9ICJsb2dfbkNvdW50X1JOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbF9ieSA9ICJwZXJjZW50Lm10IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeF90aHJlc2ggPSBjdXRfbkZlYXR1cmVfUk5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICB5X3RocmVzaCA9IGN1dF9sb2dfbkNvdW50X1JOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucyA9IDIwMCkKYGBgCgpUaGlzIGlzIHRoZSBmaWd1cmUsIGNvbG9yZWQgYnkgdGhlIHBlcmNlbnRhZ2Ugb2Ygcmlib3NvbWFsIGdlbmVzIGV4cHJlc3NlZCBpbiBjZWxsIDoKCmBgYHtyIHFjX3BhdGNod29ya19yaWJvLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4fQphcXVhcml1czo6cGxvdF9xY19mYWNzbGlrZShkZiA9IHNvYmpAbWV0YS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gIm5GZWF0dXJlX1JOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAibG9nX25Db3VudF9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfYnkgPSAicGVyY2VudC5yYiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHhfdGhyZXNoID0gY3V0X25GZWF0dXJlX1JOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeV90aHJlc2ggPSBjdXRfbG9nX25Db3VudF9STkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJpbnMgPSAyMDApCmBgYAoKVGhpcyBpcyB0aGUgZmlndXJlLCBjb2xvcmVkIGJ5IHRoZSBkb3VibGV0IGNlbGxzIHN0YXR1cyAoYGRvdWJsZXRzX2NvbnNlbnN1cy5jbGFzc2ApIDoKCmBgYHtyIHFjX3BhdGNod29ya19kb3VibGV0X2NvbnNlbnN1cywgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gOH0KYXF1YXJpdXM6OnBsb3RfcWNfZmFjc2xpa2UoZGYgPSBzb2JqQG1ldGEuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJuRmVhdHVyZV9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gImxvZ19uQ291bnRfUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sX2J5ID0gImRvdWJsZXRzX2NvbnNlbnN1cy5jbGFzcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbF9jb2xvcnMgPSBzZXROYW1lcyhubSA9IGMoVFJVRSwgRkFMU0UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXF1YXJpdXM6OmdnX2NvbG9yX2h1ZSgyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHhfdGhyZXNoID0gY3V0X25GZWF0dXJlX1JOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeV90aHJlc2ggPSBjdXRfbG9nX25Db3VudF9STkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJpbnMgPSAyMDApCmBgYAoKVGhpcyBpcyB0aGUgZmlndXJlLCBjb2xvcmVkIGJ5IHRoZSBkb3VibGV0IGNlbGxzIHN0YXR1cyAoYHNjRGJsRmluZGVyLmNsYXNzYCkgOgoKYGBge3IgcWNfcGF0Y2h3b3JrX3NjZGJsZmluZGVyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4fQphcXVhcml1czo6cGxvdF9xY19mYWNzbGlrZShkZiA9IHNvYmpAbWV0YS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gIm5GZWF0dXJlX1JOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAibG9nX25Db3VudF9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfYnkgPSAic2NEYmxGaW5kZXIuY2xhc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfY29sb3JzID0gc2V0TmFtZXMobm0gPSBjKFRSVUUsIEZBTFNFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFxdWFyaXVzOjpnZ19jb2xvcl9odWUoMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB4X3RocmVzaCA9IGN1dF9uRmVhdHVyZV9STkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHlfdGhyZXNoID0gY3V0X2xvZ19uQ291bnRfUk5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICBiaW5zID0gMjAwKQpgYGAKClRoaXMgaXMgdGhlIGZpZ3VyZSwgY29sb3JlZCBieSB0aGUgZG91YmxldCBjZWxscyBzdGF0dXMgKGBoeWJyaWRfc2NvcmUuY2xhc3NgKSA6CgpgYGB7ciBxY19wYXRjaHdvcmtfaHlicmlkX3Njb3JlLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4fQphcXVhcml1czo6cGxvdF9xY19mYWNzbGlrZShkZiA9IHNvYmpAbWV0YS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gIm5GZWF0dXJlX1JOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAibG9nX25Db3VudF9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfYnkgPSAiaHlicmlkX3Njb3JlLmNsYXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sX2NvbG9ycyA9IHNldE5hbWVzKG5tID0gYyhUUlVFLCBGQUxTRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcXVhcml1czo6Z2dfY29sb3JfaHVlKDIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeF90aHJlc2ggPSBjdXRfbkZlYXR1cmVfUk5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICB5X3RocmVzaCA9IGN1dF9sb2dfbkNvdW50X1JOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucyA9IDIwMCkKYGBgCgoKIyMjIFZpc3VhbGl6YXRpb24gYXMgcGllY2hhcnQKCkRvIGZpbHRlcmVkIGNlbGxzIGJlbG9uZyB0byBhIHBhcnRpY3VsYXIgY2VsbCB0eXBlID8KCmBgYHtyIHFjX3BpZWNoYXJ0X2NlbGxfdHlwZSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOX0Kc29iaiRhbGxfY2VsbHMgPSBUUlVFCgpwbG90X2xpc3QgPSBsaXN0KCkKCiMjIEFsbCBjZWxscwpkZiA9IHNvYmpAbWV0YS5kYXRhCmlmIChucm93KGRmKSA9PSAwKSB7CiAgcGxvdF9saXN0W1sxXV0gPSBnZ3Bsb3QoKQp9IGVsc2UgewogIHBsb3RfbGlzdFtbMV1dID0gYXF1YXJpdXM6OnBsb3RfcGllY2hhcnQoZGYgPSBkZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ljYWxfdmFyID0gImFsbF9jZWxscyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cGluZ192YXIgPSAiY2VsbF90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IGNvbG9yX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXNwbGF5X2xlZ2VuZCA9IFRSVUUpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSAiQWxsIGNlbGxzIiwKICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZShucm93KGRmKSwgImNlbGxzIikpICsKICAgIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCn0KCiMjIERvdWJsZXRzIGNvbnNlbnN1cwpkZiA9IHNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIoZG91YmxldHNfY29uc2Vuc3VzLmNsYXNzKQppZiAobnJvdyhkZikgPT0gMCkgewogIHBsb3RfbGlzdFtbMl1dID0gZ2dwbG90KCkKfSBlbHNlIHsKICBwbG90X2xpc3RbWzJdXSA9IGFxdWFyaXVzOjpwbG90X3BpZWNoYXJ0KGRmID0gZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dpY2FsX3ZhciA9ICJhbGxfY2VsbHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBpbmdfdmFyID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnMgPSBjb2xvcl9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGxheV9sZWdlbmQgPSBUUlVFKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gImRvdWJsZXRzX2NvbnNlbnN1cy5jbGFzcyIsCiAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUoc3VtKHNvYmokZG91YmxldHNfY29uc2Vuc3VzLmNsYXNzLCBuYS5ybSA9IFRSVUUpLCAiY2VsbHMiKSkgKwogICAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKfQoKIyMgcGVyY2VudC5tdApkZiA9IHNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIocGVyY2VudC5tdCA+IGN1dF9wZXJjZW50Lm10KQppZiAobnJvdyhkZikgPT0gMCkgewogIHBsb3RfbGlzdFtbM11dID0gZ2dwbG90KCkKfSBlbHNlIHsKICBwbG90X2xpc3RbWzNdXSA9IGFxdWFyaXVzOjpwbG90X3BpZWNoYXJ0KGRmID0gZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dpY2FsX3ZhciA9ICJhbGxfY2VsbHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBpbmdfdmFyID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnMgPSBjb2xvcl9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGxheV9sZWdlbmQgPSBUUlVFKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gcGFzdGUoInBlcmNlbnQubXQgPiIsIGN1dF9wZXJjZW50Lm10KSwKICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZShsZW5ndGgoZmFpbF9wZXJjZW50Lm10KSwgImNlbGxzIikpICsKICAgIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCn0KCiMjIHBlcmNlbnQucmIKZGYgPSBzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKHBlcmNlbnQucmIgPiBjdXRfcGVyY2VudC5yYikKaWYgKG5yb3coZGYpID09IDApIHsKICBwbG90X2xpc3RbWzRdXSA9IGdncGxvdCgpCn0gZWxzZSB7CiAgcGxvdF9saXN0W1s0XV0gPSBhcXVhcml1czo6cGxvdF9waWVjaGFydChkZiA9IGRmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9naWNhbF92YXIgPSAiYWxsX2NlbGxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwaW5nX3ZhciA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gY29sb3JfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXlfbGVnZW5kID0gVFJVRSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHBhc3RlKCJwZXJjZW50LnJiID4iLCBjdXRfcGVyY2VudC5yYiksCiAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUobGVuZ3RoKGZhaWxfcGVyY2VudC5yYiksICJjZWxscyIpKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQp9CgojIyBsb2dfbkNvdW50X1JOQQpkZiA9IHNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIobG9nX25Db3VudF9STkEgPCBjdXRfbG9nX25Db3VudF9STkEpCmlmIChucm93KGRmKSA9PSAwKSB7CiAgcGxvdF9saXN0W1s1XV0gPSBnZ3Bsb3QoKQp9IGVsc2UgewogIHBsb3RfbGlzdFtbNV1dID0gYXF1YXJpdXM6OnBsb3RfcGllY2hhcnQoZGYgPSBkZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ljYWxfdmFyID0gImFsbF9jZWxscyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cGluZ192YXIgPSAiY2VsbF90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IGNvbG9yX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXNwbGF5X2xlZ2VuZCA9IFRSVUUpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBwYXN0ZSgibG9nX25Db3VudF9STkEgPCIsIHJvdW5kKGN1dF9sb2dfbkNvdW50X1JOQSwgMikpLAogICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKGxlbmd0aChmYWlsX2xvZ19uQ291bnRfUk5BKSwgImNlbGxzIikpICsKICAgIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCn0KCiMjIG5GZWF0dXJlX1JOQQpkZiA9IHNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIobkZlYXR1cmVfUk5BIDwgY3V0X25GZWF0dXJlX1JOQSkKaWYgKG5yb3coZGYpID09IDApIHsKICBwbG90X2xpc3RbWzZdXSA9IGdncGxvdCgpCn0gZWxzZSB7CiAgcGxvdF9saXN0W1s2XV0gPSBhcXVhcml1czo6cGxvdF9waWVjaGFydChkZiA9IGRmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9naWNhbF92YXIgPSAiYWxsX2NlbGxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwaW5nX3ZhciA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gY29sb3JfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXlfbGVnZW5kID0gVFJVRSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHBhc3RlKCJuRmVhdHVyZV9STkEgPCIsIHJvdW5kKGN1dF9uRmVhdHVyZV9STkEsIDIpKSwKICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZShsZW5ndGgoZmFpbF9uRmVhdHVyZV9STkEpLCAiY2VsbHMiKSkgKwogICAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKfQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDMpICsKICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikgJgogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKYGBge3IgY2xlYW5fcWNfNCwgZWNobyA9IEZBTFNFfQpzb2JqJGFsbF9jZWxscyA9IE5VTEwKCnJtKHBsb3RfbGlzdCwgZGYpCmBgYAoKCiMjIyBEb3VibGV0IGNlbGxzIHN0YXR1cwoKV2UgY2FuIGNvbXBhcmUgZG91YmxldCBkZXRlY3Rpb24gbWV0aG9kcyB3aXRoIGEgVmVubiBkaWFncmFtIDogCgpgYGB7ciBxY192ZW5uX2RvdWJsZXQsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNn0KZ2d2ZW5uOjpnZ3Zlbm4obGlzdChoeWJyaWQgPSBmYWlsX2RvdWJsZXRzX2h5YnJpZCwKICAgICAgICAgICAgICAgICAgICBzY0RibEZpbmRlciA9IGZhaWxfZG91YmxldHNfc2NEYmxGaW5kZXIpLCAKICAgICAgICAgICAgICAgZmlsbF9jb2xvciA9IGMoIiMwMDczQzJGRiIsICIjRUZDMDAwRkYiKSwKICAgICAgICAgICAgICAgc3Ryb2tlX3NpemUgPSAwLjUsIHNldF9uYW1lX3NpemUgPSA0KSArCiAgZ2dwbG90Mjo6Z2d0aXRsZShsYWJlbCA9ICJEb3VibGV0IGNlbGxzIikgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKQpgYGAKCldlIHZpc3VhbGl6ZSBjZWxscyBhbm5vdGF0aW9uIGZvciBkb3VibGV0cyA6CgpgYGB7ciBkb3VibGV0c19wcm9qLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDYsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpwbG90X2xpc3QgPSBsaXN0KCkKCiMgc2NEYmxGaW5kZXIuY2xhc3MKc29iaiRmYWlsID0gaWZlbHNlKHNvYmokc2NEYmxGaW5kZXIuY2xhc3MsCiAgICAgICAgICAgICAgICAgICB5ZXMgPSBhcy5jaGFyYWN0ZXIoc29iaiRjZWxsX3R5cGUpLCBubyA9IE5BKQpzb2JqJGZhaWwgPSBmYWN0b3Ioc29iaiRmYWlsLCBsZXZlbHMgPSBjKGxldmVscyhzb2JqJGNlbGxfdHlwZSksIE5BKSkKCnBsb3RfbGlzdFtbMV1dID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImZhaWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9ICJncmF5ODAiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAic2NEYmxGaW5kZXIuY2xhc3MiLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoc3VtKHNvYmokc2NEYmxGaW5kZXIuY2xhc3MsIG5hLnJtID0gVFJVRSksICIgY2VsbHMiKSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBTZXVyYXQ6Ok5vTGVnZW5kKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgojIGh5YnJpZF9zY29yZS5jbGFzcwpzb2JqJGZhaWwgPSBpZmVsc2Uoc29iaiRoeWJyaWRfc2NvcmUuY2xhc3MsCiAgICAgICAgICAgICAgICAgICB5ZXMgPSBhcy5jaGFyYWN0ZXIoc29iaiRjZWxsX3R5cGUpLCBubyA9IE5BKQpzb2JqJGZhaWwgPSBmYWN0b3Ioc29iaiRmYWlsLCBsZXZlbHMgPSBjKGxldmVscyhzb2JqJGNlbGxfdHlwZSksIE5BKSkKCnBsb3RfbGlzdFtbMl1dID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImZhaWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9ICJncmF5ODAiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiaHlicmlkX3Njb3JlLmNsYXNzIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKHN1bShzb2JqJGh5YnJpZF9zY29yZS5jbGFzcywgbmEucm0gPSBUUlVFKSwgIiBjZWxscyIpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnNvYmokZmFpbCA9IE5VTEwKCiMgUGxvdApwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBucm93ID0gMSkKYGBgCgpXaGF0IGlzIHRoZSBjb21wb3NpdGlvbiBvZiBkb3VibGV0IGNlbGxzID8gV2UganVzdCBsb29rIGF0IHNjb3JlIGZvciBlYWNoIGNlbGwgdHlwZS4KCmBgYHtyIGRvdWJsZXRzX2NvbXBvc2l0aW9uLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0Kc29iaiRvcmlnLmlkZW50LmRvdWJsZXRzID0gY2FzZV93aGVuKGlzLm5hKHNvYmokZG91YmxldHNfY29uc2Vuc3VzLmNsYXNzKSB+ICJiYWQgcXVhbGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqJGRvdWJsZXRzX2NvbnNlbnN1cy5jbGFzcyA9PSBUUlVFIH4gcGFzdGUwKHNvYmokb3JpZy5pZGVudCwgIiBkb3VibGV0cyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29iaiRkb3VibGV0c19jb25zZW5zdXMuY2xhc3MgPT0gRkFMU0UgfiAibm90IGRvdWJsZXQiKQpzb2JqJG9yaWcuaWRlbnQuZG91YmxldHMgPSBmYWN0b3Ioc29iaiRvcmlnLmlkZW50LmRvdWJsZXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYyhwYXN0ZTAoYXMuY2hhcmFjdGVyKHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWFudCksICIgZG91YmxldHMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImJhZCBxdWFsaXR5IiwgIm5vdCBkb3VibGV0IikpCgpkb3VibGV0c19jb21wbyA9IGZ1bmN0aW9uKHNjb3JlMSwgc2NvcmUyKSB7CiAgdHlwZTEgPSB1bmxpc3QobGFwcGx5KHN0cmluZ3I6OnN0cl9zcGxpdChzY29yZTEsIHBhdHRlcm4gPSAic2NvcmVfIiksIGBbW2AsIDIpKQogIHR5cGUyID0gdW5saXN0KGxhcHBseShzdHJpbmdyOjpzdHJfc3BsaXQoc2NvcmUyLCBwYXR0ZXJuID0gInNjb3JlXyIpLCBgW1tgLCAyKSkKICAKICBpZiAodHlwZTEgPT0gdHlwZTIpIHsKICAgIHRoZV90aXRsZSA9ICJIb21vdHlwaWMgZG91YmxldCIKICAgIHRoZV9zdWJ0aXRsZSA9IHR5cGUxCiAgICBzY29yZTEgPSAibG9nX25Db3VudF9STkEiCiAgfSBlbHNlIHsKICAgIHRoZV90aXRsZSA9ICJIZXRlcm90eXBpYyBkb3VibGV0IgogICAgdGhlX3N1YnRpdGxlID0gcGFzdGUodHlwZTEsIHR5cGUyLCBzZXAgPSAiICsgIikKICB9CiAgCiAgcCA9IHNvYmpAbWV0YS5kYXRhICU+JQogICAgZHBseXI6OmFycmFuZ2UoZGVzYyhvcmlnLmlkZW50LmRvdWJsZXRzKSkgJT4lCiAgICBnZ3Bsb3QyOjpnZ3Bsb3QoLiwgYWVzKHggPSBldmFsKHBhcnNlKHRleHQgPSBzY29yZTEpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGV2YWwocGFyc2UodGV4dCA9IHNjb3JlMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBvcmlnLmlkZW50LmRvdWJsZXRzKSkgKwogICAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMC4yNSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoc2FtcGxlX2luZm8kY29sb3IsICJncmF5OTAiLCAiZ3JheTYwIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYyhwYXN0ZTAoYXMuY2hhcmFjdGVyKHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWFudCksICIgZG91YmxldHMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJiYWQgcXVhbGl0eSIsICJub3QgZG91YmxldCIpKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSBzY29yZTEsIHkgPSBzY29yZTIsCiAgICAgICAgICAgICAgICAgIHRpdGxlID0gdGhlX3RpdGxlLCBzdWJ0aXRsZSA9IHRoZV9zdWJ0aXRsZSkgKwogICAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQogIAogIHJldHVybihwKQp9CmBgYAoKYGBge3IgZG91YmxldHNfcGF0Y2h3b3JrX3ByZXAsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpzY29yZV9jb2x1bW5zID0gZ3JlcCh4ID0gY29sbmFtZXMoc29iakBtZXRhLmRhdGEpLAogICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIl5zY29yZSIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gVFJVRSkKY29tYmluYXRpb25zID0gZXhwYW5kLmdyaWQoc2NvcmVfY29sdW1ucywgc2NvcmVfY29sdW1ucykgJT4lCiAgYXBwbHkoLiwgMSwgc29ydCkgJT4lIHQoKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkKY29tYmluYXRpb25zID0gY29tYmluYXRpb25zWyFkdXBsaWNhdGVkKGNvbWJpbmF0aW9ucyksIF0KCnBsb3RfbGlzdCA9IGFwcGx5KGNvbWJpbmF0aW9ucywgMSwgRlVOID0gZnVuY3Rpb24oZWxlbSkgewogIGRvdWJsZXRzX2NvbXBvKGVsZW1bMV0sIGVsZW1bMl0pCn0pCgpzb2JqJG9yaWcuaWRlbnQuZG91YmxldHMgPSBOVUxMCmBgYAoKCmBgYHtyIGRvdWJsZXRzX3BhdGNod29yaywgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4MCwgZm9sZF9wbG90ID0gVFJVRX0KcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDQpICsKICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikgJgogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKIyMgU2F2ZQoKV2UgY291bGQgc2F2ZSB0aGlzIG9iamVjdCBiZWZvcmUgZmlsdGVyaW5nIChyZW1vdmUgYGV2YWwgPSBGQUxTRWApIDoKCmBgYHtyIHNhdmVfc29ial91bmZpbHRlcmVkX2Fubm90YXRlZCwgZXZhbCA9IEZBTFNFfQpzYXZlUkRTKHNvYmosIHBhc3RlMChvdXRfZGlyLCAiL2RhdGFzZXRzLyIsIHNhbXBsZV9uYW1lLCAiX3NvYmpfdW5maWx0ZXJlZC5yZHMiKSkKYGBgCgoKIyBGaWx0ZXJpbmcKCldlIHJlbW92ZSA6CgoqIGNlbGxzIHdpdGggYSBudW1iZXIgb2YgVU1JIGxvd2VyIHRoYW4gYHIgY3V0X2xvZ19uQ291bnRfUk5BYAoqIGNlbGxzIGV4cHJlc3NpbmcgYSBudW1iZXIgb2YgZ2VuZXMgbG93ZXIgdGhhbiBgciBjdXRfbkZlYXR1cmVfUk5BYAoqIGNlbGxzIGhhdmluZyBtb3JlIHRoYW4gYHIgY3V0X3BlcmNlbnQubXRgIFwlIG9mIFVNSSByZWxhdGVkIHRvIG1pdG9jaG9uZHJpYWwgZ2VuZXMKKiBjZWxscyBoYXZpbmcgbW9yZSB0aGFuIGByIGN1dF9wZXJjZW50LnJiYCBcJSBvZiBVTUkgcmVsYXRlZCB0byByaWJvc29tYWwgZ2VuZXMKKiBkb3VibGV0IGNlbGxzIGRldGVjdGVkIHdpdGggYm90aCBtZXRob2QgKCoqc2NEYmxGaW5kZXIqKiBhbmQgKipzY2RzLWh5YnJpZCoqKQoKCmBgYHtyIGZpbHRlcl9jZWxsc30Kc29iaiA9IHN1YnNldChzb2JqLCBpbnZlcnQgPSBUUlVFLAogICAgICAgICAgICAgIGNlbGxzID0gdW5pcXVlKGMoZmFpbF9sb2dfbkNvdW50X1JOQSwgZmFpbF9uRmVhdHVyZV9STkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWlsX3BlcmNlbnQubXQsIGZhaWxfcGVyY2VudC5yYiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhaWxfZG91YmxldHNfY29uc2Vuc3VzKSkpCnNvYmoKYGBgCgpgYGB7ciBjbGVhbl9maWx0ZXIsIGVjaG8gPSBGQUxTRX0Kcm0oZmFpbF9kb3VibGV0c19jb25zZW5zdXMsIGZhaWxfZG91YmxldHNfc2NEYmxGaW5kZXIsIGZhaWxfZG91YmxldHNfaHlicmlkLAogICBmYWlsX3BlcmNlbnQubXQsIGZhaWxfcGVyY2VudC5yYiwgZmFpbF9sb2dfbkNvdW50X1JOQSwgZmFpbF9uRmVhdHVyZV9STkEsCiAgIGN1dF9wZXJjZW50Lm10LCBjdXRfcGVyY2VudC5yYiwgY3V0X2xvZ19uQ291bnRfUk5BLCBjdXRfbkZlYXR1cmVfUk5BKQpgYGAKCgojIFBvc3QtZmlsdGVyaW5nIHByb2Nlc3NpbmcKCiMjIE5vcm1hbGl6YXRpb24KCldlIG5vcm1hbGl6ZSB0aGUgY291bnQgbWF0cml4IGZvciByZW1haW5pbmcgY2VsbHMgOgoKYGBge3Igbm9ybWFsaXphdGlvbl8zfQpzb2JqID0gU2V1cmF0OjpOb3JtYWxpemVEYXRhKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiKQoKc29iaiA9IFNldXJhdDo6RmluZFZhcmlhYmxlRmVhdHVyZXMoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZlYXR1cmVzID0gMzAwMCkKc29iagpgYGAKCiMjIFByb2plY3Rpb24KCldlIHBlcmZvcm0gYSBQQ0EgOgoKYGBge3IgcGNhLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDR9CnNvYmogPSBhcXVhcml1czo6ZGltZW5zaW9uc19yZWR1Y3Rpb24oc29iaiA9IHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAicGNhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhfZGltcyA9IDEwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpClNldXJhdDo6RWxib3dQbG90KHNvYmosIG5kaW1zID0gMTAwLCByZWR1Y3Rpb24gPSAiUk5BX3BjYSIpCmBgYAoKCldlIGdlbmVyYXRlIGEgdFNORSBhbmQgYSBVTUFQIHdpdGggMjAgcHJpbmNpcGFsIGNvbXBvbmVudHMgOgoKYGBge3IgdHNuZV91bWFwfQpuZGltcyA9IDIwCnNvYmogPSBTZXVyYXQ6OlJ1blRTTkUoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAiUk5BX3BjYSIsCiAgICAgICAgICAgICAgICAgICAgICAgZGltcyA9IDE6bmRpbXMsCiAgICAgICAgICAgICAgICAgICAgICAgc2VlZC51c2UgPSAxMzM3TCwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24ubmFtZSA9IHBhc3RlMCgiUk5BX3BjYV8iLCBuZGltcywgIl90c25lIikpCgpzb2JqID0gU2V1cmF0OjpSdW5VTUFQKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gIlJOQV9wY2EiLAogICAgICAgICAgICAgICAgICAgICAgIGRpbXMgPSAxOm5kaW1zLAogICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gMTMzN0wsCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uLm5hbWUgPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdW1hcCIpKQpgYGAKCgojIyBBbm5vdGF0aW9uCgpXZSBhbm5vdGF0ZSBjZWxscyBmb3IgY2VsbCB0eXBlLCB3aXRoIHRoZSBuZXcgbm9ybWFsaXplZCBleHByZXNzaW9uIG1hdHJpeCA6CgpgYGB7ciBjZWxsX3R5cGVfMiwgdGltZV9pdCA9IFRSVUV9CnNjb3JlX2NvbHVtbnMgPSBncmVwKHggPSBjb2xuYW1lcyhzb2JqQG1ldGEuZGF0YSksIHBhdHRlcm4gPSAiXnNjb3JlIiwgdmFsdWUgPSBUUlVFKQpzb2JqQG1ldGEuZGF0YVssIHNjb3JlX2NvbHVtbnNdID0gTlVMTApzb2JqJGNlbGxfdHlwZSA9IE5VTEwKCnNvYmogPSBhcXVhcml1czo6Y2VsbF9hbm5vdF9jdXN0b20oc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXduYW1lID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFya2VycyA9IGNlbGxfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VfbmVnYXRpdmUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFkZF9zY29yZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUpCgpzb2JqJGNlbGxfdHlwZSA9IGZhY3Rvcihzb2JqJGNlbGxfdHlwZSwgbGV2ZWxzID0gbmFtZXMoY2VsbF9tYXJrZXJzKSkKCmNvbG5hbWVzKHNvYmpAbWV0YS5kYXRhKSA9IHN0cmluZ3I6OnN0cl9yZXBsYWNlX2FsbChzdHJpbmcgPSBjb2xuYW1lcyhzb2JqQG1ldGEuZGF0YSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIiAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiXyIpCgp0YWJsZShzb2JqJGNlbGxfdHlwZSkKYGBgCgoKVG8ganVzdGlmeSBjZWxsIHR5cGUgYW5ub3RhdGlvbiwgd2UgY2FuIG1ha2UgYSBkb3RwbG90IDoKCmBgYHtyIGRvdHBsb3RfY2VsbF90eXBlX3Nob3J0MiwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA5fQptYXJrZXJzID0gYygiUFRQUkMiLCB1bmlxdWUodW5saXN0KGRvdHBsb3RfbWFya2Vyc1tsZXZlbHMoc29iaiRjZWxsX3R5cGUpXSkpKQptYXJrZXJzID0gbWFya2Vyc1ttYXJrZXJzICVpbiUgcm93bmFtZXMoc29iaildCgphcXVhcml1czo6cGxvdF9kb3RwbG90KHNvYmosIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWUgPSAiY2VsbF90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICBtYXJrZXJzID0gbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICBuYl9obGluZSA9IDApICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6Ojpjb2xvcl9nZW5lKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwKICAgICAgICAgICAgICAgICBsZWdlbmQuYm94ID0gInZlcnRpY2FsIiwKICAgICAgICAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQpgYGAKCldlIGNhbiBtYWtlIGEgYmFycGxvdCB0byBzZWUgdGhlIGNvbXBvc2l0aW9uIG9mIGVhY2ggZGF0YXNldCwgYW5kIHZpc3VhbGl6ZSBjZWxsIHR5cGVzIG9uIHRoZSBwcm9qZWN0aW9uLgoKYGBge3IgYmFycGxvdF9jZWxsdHlwZTIsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA4LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KZGZfcHJvcG9ydGlvbiA9IGFzLmRhdGEuZnJhbWUocHJvcC50YWJsZSh0YWJsZShzb2JqJG9yaWcuaWRlbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29iaiRjZWxsX3R5cGUpKSkKY29sbmFtZXMoZGZfcHJvcG9ydGlvbikgPSBjKCJvcmlnLmlkZW50IiwgImNlbGxfdHlwZSIsICJmcmVxIikKCnF1YW50aWYgPSB0YWJsZShzb2JqJG9yaWcuaWRlbnQpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygib3JpZy5pZGVudCIsICJuYl9jZWxscyIpKQoKIyBQbG90CnBsb3RfbGlzdCA9IGxpc3QoKQoKcGxvdF9saXN0W1syXV0gPSBhcXVhcml1czo6cGxvdF9iYXJwbG90KGRmID0gZGZfcHJvcG9ydGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAib3JpZy5pZGVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gImZyZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBnZ3Bsb3QyOjpwb3NpdGlvbl9maWxsKCkpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkNlbGwgdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gY29sb3JfbWFya2Vyc1tsZXZlbHMoZGZfcHJvcG9ydGlvbiRjZWxsX3R5cGUpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBsZXZlbHMoZGZfcHJvcG9ydGlvbiRjZWxsX3R5cGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gcXVhbnRpZiwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gb3JpZy5pZGVudCwgeSA9IDEuMDUsIGxhYmVsID0gbmJfY2VsbHMpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IDApCgpwbG90X2xpc3RbWzFdXSA9IFNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAiUk5BX3BjYV8yMF90c25lIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSB1bmxpc3QoY29sb3JfbWFya2VycyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHNhbXBsZV9uYW1lLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAobmNvbChzb2JqKSwgIiBjZWxscyIpKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpICsgU2V1cmF0OjpOb0F4ZXMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5yb3cgPSAxLCB3aWR0aHMgPSBjKDYsIDEpKQpgYGAKCgojIyMgQ2VsbCBjeWNsZQoKV2UgYW5ub3RhdGUgY2VsbHMgZm9yIGNlbGwgY3ljbGUgcGhhc2UgOgoKYGBge3IgY2VsbF9jeWNsZTIsIHRpbWVfaXQgPSBUUlVFfQpjY19jb2x1bW5zID0gYXF1YXJpdXM6OmFkZF9jZWxsX2N5Y2xlKHNvYmogPSBzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BlY2llc19yZHggPSAiaHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJQUEFSQU0gPSBjbClAbWV0YS5kYXRhWywgYygiU2V1cmF0LlBoYXNlIiwgIlBoYXNlIildCgpzb2JqJFNldXJhdC5QaGFzZSA9IGNjX2NvbHVtbnMkU2V1cmF0LlBoYXNlCnNvYmokY3ljbG9uZS5QaGFzZSA9IGNjX2NvbHVtbnMkUGhhc2UKCnRhYmxlKHNvYmokU2V1cmF0LlBoYXNlLCBzb2JqJGN5Y2xvbmUuUGhhc2UpCmBgYAoKCldlIHZpc3VhbGl6ZSBjZWxsIGN5Y2xlIG9uIHRoZSBwcm9qZWN0aW9uIDoKCmBgYHtyIHNlZV9jZWxsX2N5Y2xlMiwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA3LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KcGxvdF9saXN0ID0gbGlzdCgpCgpwbG90X2xpc3RbWzJdXSA9IFNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJTZXVyYXQuUGhhc2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAiUk5BX3BjYV8yMF90c25lIikgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiQ2VsbCBDeWNsZSBQaGFzZSIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJTZXVyYXQuUGhhc2UiKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpICsgU2V1cmF0OjpOb0F4ZXMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnBsb3RfbGlzdFtbMV1dID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImN5Y2xvbmUuUGhhc2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAiUk5BX3BjYV8yMF90c25lIikgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiQ2VsbCBDeWNsZSBQaGFzZSIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJjeWNsb25lLlBoYXNlIikgKwogIFNldXJhdDo6Tm9MZWdlbmQoKSArIFNldXJhdDo6Tm9BeGVzKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBucm93ID0gMSkKYGBgCgoKIyMgQ2x1c3RlcmluZwoKV2UgbWFrZSBhIGhpZ2hseSByZXNvbHV0aXZlIGNsdXN0ZXJpbmcgOgoKYGBge3IgY2x1c3RlcmluZ30Kc29iaiA9IFNldXJhdDo6RmluZE5laWdoYm9ycyhzb2JqLCByZWR1Y3Rpb24gPSAiUk5BX3BjYSIsIGRpbXMgPSBjKDE6bmRpbXMpKQpzb2JqID0gU2V1cmF0OjpGaW5kQ2x1c3RlcnMoc29iaiwgcmVzb2x1dGlvbiA9IDIpCgp0YWJsZShzb2JqJHNldXJhdF9jbHVzdGVycykKYGBgCgoKIyBWaXN1YWxpemF0aW9uCgojIyBDZWxsIHR5cGUKCldlIGNhbiB2aXN1YWxpemUgdGhlIGNlbGwgdHlwZSA6CgpgYGB7ciBzZWVfY2VsbF90eXBlLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDYsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQp0c25lID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gcGFzdGUwKCJSTkFfcGNhXyIsIG5kaW1zLCAiX3RzbmUiKSwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsgZ2dwbG90Mjo6Z2d0aXRsZSgidFNORSIpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnVtYXAgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiY2VsbF90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdW1hcCIpLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBnZ3Bsb3QyOjpnZ3RpdGxlKCJVTUFQIikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgp0c25lIHwgdW1hcApgYGAKCiMjIENlbGwgY3ljbGUKCldlIGNhbiB2aXN1YWxpemUgdGhlIGNlbGwgY3ljbGUsIGZyb20gU2V1cmF0IDoKCmBgYHtyIHNlZV9jY19TZXVyYXQsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNiwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnRzbmUgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiU2V1cmF0LlBoYXNlIiwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdHNuZSIpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIGdncGxvdDI6OmdndGl0bGUoInRTTkUiKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgp1bWFwID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gIlNldXJhdC5QaGFzZSIsCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gcGFzdGUwKCJSTkFfcGNhXyIsIG5kaW1zLCAiX3VtYXAiKSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBnZ3Bsb3QyOjpnZ3RpdGxlKCJVTUFQIikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgp0c25lIHwgdW1hcApgYGAKCgpXZSBjYW4gdmlzdWFsaXplIHRoZSBjZWxsIGN5Y2xlLCBmcm9tIGN5Y2xvbmUgOgoKYGBge3Igc2VlX2NjX2N5Y2xvbmUsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNiwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnRzbmUgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiY3ljbG9uZS5QaGFzZSIsCiAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gcGFzdGUwKCJSTkFfcGNhXyIsIG5kaW1zLCAiX3RzbmUiKSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBnZ3Bsb3QyOjpnZ3RpdGxlKCJ0U05FIikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKdW1hcCA9IFNldXJhdDo6RGltUGxvdChzb2JqLCBncm91cC5ieSA9ICJjeWNsb25lLlBoYXNlIiwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdW1hcCIpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIGdncGxvdDI6OmdndGl0bGUoIlVNQVAiKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnRzbmUgfCB1bWFwCmBgYAoKCiMjIENsdXN0ZXJzCgpXZSB2aXN1YWxpemUgdGhlIGNsdXN0ZXJpbmcgOgoKYGBge3Igc2VlX2NsdXN0ZXJzLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDYsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQp0c25lID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsIGxhYmVsID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdHNuZSIpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIGdncGxvdDI6OmdndGl0bGUoInRTTkUiKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgp1bWFwID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsIGxhYmVsID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBwYXN0ZTAoIlJOQV9wY2FfIiwgbmRpbXMsICJfdW1hcCIpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIGdncGxvdDI6OmdndGl0bGUoIlVNQVAiKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnRzbmUgfCB1bWFwCmBgYAoKCiMjIEdlbmUgZXhwcmVzc2lvbgoKV2UgdmlzdWFsaXplIGFsbCBjZWxsIHR5cGVzIG1hcmtlcnMgb24gdGhlIHRTTkUgOgoKYGBge3IgcGxvdF9nZW5lcywgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAyMH0KbWFya2VycyA9IGRvdHBsb3RfbWFya2VycyAlPiUgdW5saXN0KCkgJT4lIHVubmFtZSgpCm1hcmtlcnMgPSBtYXJrZXJzW21hcmtlcnMgJWluJSByb3duYW1lcyhzb2JqKV0KCnBsb3RfbGlzdCA9IGxhcHBseShtYXJrZXJzLAogICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24ob25lX2dlbmUpIHsKICAgICAgICAgICAgICAgICAgICAgcCA9IFNldXJhdDo6RmVhdHVyZVBsb3Qoc29iaiwgZmVhdHVyZXMgPSBvbmVfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gcGFzdGUwKCJSTkFfcGNhXyIsIG5kaW1zLCAiX3RzbmUiKSkgKwogICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBvbmVfZ2VuZSkgKwogICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICAgICAgICAgICAgICAgICAgICAgICBTZXVyYXQ6Ok5vQXhlcygpCiAgICAgICAgICAgICAgICAgICAgIHJldHVybihwKQogICAgICAgICAgICAgICAgICAgfSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSA0KQpgYGAKCgojIFNhdmUKCldlIHNhdmUgdGhlIGFubm90YXRlZCBhbmQgZmlsdGVyZWQgU2V1cmF0IG9iamVjdCA6CgpgYGB7ciBzYXZlX3NvYmpfZmlsdGVyZWRfYW5ub3RhdGVkfQpzYXZlUkRTKHNvYmosIGZpbGUgPSBwYXN0ZTAob3V0X2RpciwgIi9kYXRhc2V0cy8iLCBzYW1wbGVfbmFtZSwgIl9zb2JqX2ZpbHRlcmVkLnJkcyIpKQpgYGAKCgojIFIgc2Vzc2lvbgoKYGBge3Igc2Vzc2lvbmluZm8sIGVjaG8gPSBGQUxTRSwgZm9sZF9vdXRwdXQgPSBUUlVFfQpzZXNzaW9uSW5mbygpCmBgYAo=